[并差集][lca][dfs] Jzoj P5798 树

Posted comfortable

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[并差集][lca][dfs] Jzoj P5798 树相关的知识,希望对你有一定的参考价值。

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 树的主要内容,如果未能解决你的问题,请参考以下文章

[带权并差集] Jzoj P1503 体育场

[数论][LCA][并查集]JZOJ 5782 城市猎人

[差分][倍增lca][tarjan] Jzoj P3325 压力

[JZOJ 5852] 相交

[jzoj 5661] 药香沁鼻 解题报告 (DP+dfs序)

[DFS]JZOJ 5916 flow