1. 程式人生 > >[並差集][lca][dfs] Jzoj P5798 樹

[並差集][lca][dfs] Jzoj P5798 樹

100% dset 此外 out oge 表示 output 技術 code

Description

我們有一顆從1到n編號的n個結點的樹,此外,您將從樹中獲得M個節點對,形式為(a1,b1),(a2,b2),…(am,bm).
我們需要給每一條邊定向,使得每一對節點對存在一條從ai到bi或從bi到ai的路徑。
現在要求方案數,對10^9+7取mod即可。

Input

第一行兩個整數,n,m
接下來n-1行,每一行兩個整數,描述一條樹邊。
接下來m行,描述ai,bi

Output

輸出一個整數,表示方案數對10^9+7取mod

Sample Input

input1:
4 1
1 2
2 3
3 4
2 4
input2:
7 2
1 2
1 3
4 2
2 5
6 5
5 7
1 7
2 6 
input3:
4 3
1 2
1 3
1 4
2 3
2 4
3 4 

Sample Output

output1:
4
output2:
8
output3:
0

Data Constraint

對於前20%的數據,保證是一條鏈。
另有40%的數據,n,m<=5000
對於100%的數據,n,m<=300000

題解

  • 技術分享圖片
  • 發現,對於一對點(ai,bi)
  • 只要其中一條被確定了,其它都被確定了,而且黃色和紅色的方向相反
  • 可以用並差集,兩個點之間有關系的可以打入同一個並差集裏
  • 這樣最後的答案就是2^並差集的個數
  • 其中,並差集要按秩合並
  • 現在,考慮一下0的情況
  • 在維護並差集時還要維護一個信息,就是當前方向與父親的方向是否相反,這個異或一下就好了
  • 那麽對於兩個點(a,b),如果要使它們可行,它們的路徑要相反
  • 因為,按照並差集每次會將下面的某個點連到最上面的祖先,那麽求會產生上圖(ai,bi)的情況
  • 就要方向相反

代碼

  1 #include <cstdio>
  2 #include <iostream>
  3 #include <cstring>
  4 using namespace std;
  5 const long long mo=1e9+7;
  6 struct edge {int to,from; }e[300010*2];
  7 int fa[300010],p[300010
][3],f[300010][20],deep[300010],w[300010],head[300010],cnt,n,m; 8 bool boo; 9 void insert(int x,int y) { e[++cnt].to=y; e[cnt].from=head[x]; head[x]=cnt; } 10 void dfs(int x,int fa) 11 { 12 deep[x]=deep[fa]+1; 13 for (int i=head[x];i;i=e[i].from) 14 { 15 int v=e[i].to; 16 if (v==fa) continue; 17 f[v][0]=x; 18 dfs(v,x); 19 } 20 } 21 int getlca(int x,int y) 22 { 23 if (deep[x]>deep[y]) swap(x,y); 24 for (int i=18;i>=0;i--) 25 if (deep[f[y][i]]>=deep[x]) 26 y=f[y][i]; 27 if (x==y) return x; 28 for (int i=18;i>=0;i--) 29 if (f[x][i]!=f[y][i]) 30 x=f[x][i],y=f[y][i]; 31 return f[x][0]; 32 } 33 int getfather(int x) 34 { 35 if (fa[x]==x) return x; 36 int d=fa[x]; 37 fa[x]=getfather(fa[x]); 38 w[x]=w[x]^w[d]; 39 return fa[x]; 40 } 41 void together(int x,int lca) 42 { 43 x=getfather(x); 44 while (deep[x]-2>=deep[lca]) 45 { 46 int v=f[x][0]; 47 v=getfather(v); 48 fa[x]=v; 49 x=getfather(x); 50 } 51 } 52 int main() 53 { 54 freopen("usmjeri.in","r",stdin); 55 freopen("usmjeri.out","w",stdout); 56 scanf("%d%d",&n,&m); 57 for (int i=1;i<=n-1;i++) 58 { 59 int u,v; 60 scanf("%d%d",&u,&v); 61 insert(u,v),insert(v,u); 62 } 63 dfs(1,0); f[1][0]=1; 64 for (int i=1;i<=18;i++) 65 for (int j=1;j<=n;j++) 66 f[j][i]=f[f[j][i-1]][i-1]; 67 for (int i=1;i<=n;i++) fa[i]=i; 68 for (int i=1;i<=m;i++) 69 { 70 int u,v; 71 scanf("%d%d",&u,&v); 72 int lca=getlca(u,v); 73 together(u,lca),together(v,lca); 74 p[i][0]=u,p[i][1]=v,p[i][2]=lca; 75 } 76 boo=1; 77 for (int i=1;i<=m;i++) 78 { 79 int x=p[i][0],y=p[i][1],lca=p[i][2]; 80 if (x==lca||y==lca) continue; 81 int u=getfather(x),v=getfather(y); 82 if (u==v) 83 { 84 if ((w[x]^w[y])!=1) 85 { 86 printf("0"); 87 return 0; 88 } 89 continue; 90 } 91 fa[u]=v,w[u]=1^w[y]^w[x]; 92 } 93 int ans=1; 94 for (int i=2;i<=n;i++) 95 { 96 getfather(i); 97 if (fa[i]==i) (ans*=2)%=mo; 98 } 99 printf("%d",ans); 100 return 0; 101 }

[並差集][lca][dfs] Jzoj P5798 樹