闇の連鎖
(yam.pas/c/cpp)
题目描述
传说中的暗之连锁被人们称为 Dark。Dark 是人类内心的
黑暗的产物,古今中外的勇者们都试图打倒它。经过研究,
你发现 Dark 呈现无向图的结构,图中有 N 个节点和两类边,
一类边被称为主要边,而另一类被称为附加边。Dark 有 N – 1
条主要边,并且 Dark 的任意两个节点之间都存在一条只由主
要边构成的路径。另外,Dark 还有 M 条附加边。
你的任务是把 Dark 斩为不连通的两部分。一开始 Dark
的附加边都处于无敌状态,你只能选择一条主要边切断。一
旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切
断。但是你的能力只能再切断 Dark 的一条附加边。现在你想要知道,一共有多少种方案可
以击败 Dark。注意,就算你第一步切断主要边之后就已经把 Dark 斩为两截,你也需要切断
一条附加边才算击败了 Dark。
输入格式
第一行包含两个整数 N 和 M。
之后 N – 1 行,每行包括两个整数 A 和 B,表示 A 和 B 之间有一条主要边。
之后 M 行以同样的格式给出附加边。
输出格式
输出一个整数表示答案。
显然,这个图上的点与主要边构成了一棵树。当加入一条附加边时,会构成环。如果我们第一次砍主要边的时候砍的是$x$->$y$路径上的某条边,那么之后再砍的附加边一定是$<x,y>$。
这样,我们的题目就转换成了一个树上差分的问题。附加边$<x,y>$会影响x到y路径上的所有边。把这些边经过的次数(受影响的次数)进行统计。若第一次砍的是覆盖0次的,那么第二步可任意切断一条附加边;若一次砍的 是覆盖1次的,第二步方法唯一;其他答案在第二步均没有其他任何方法。
实现的时候dfs和lca查询写错了两个细节,结果调了很久...气orz
Code
1 #include<cstdio> 2 #include<algorithm> 3 #include<queue> 4 #include<cmath> 5 #define maxn 100090 6 7 using namespace std; 8 9 int n,m,tot,t,ans; 10 int head[maxn],d[maxn],f[maxn][30],val[maxn]; 11 struct node{ 12 int to,next; 13 }edge[maxn*2]; 14 15 void add(int x,int y) 16 { 17 edge[++tot].to=y; 18 edge[tot].next=head[x]; 19 head[x]=tot; 20 } 21 22 void init() 23 { 24 queue<int>q; 25 q.push(1),d[1]=1; 26 while(!q.empty()) 27 { 28 int u=q.front();q.pop(); 29 for(int i=head[u];i;i=edge[i].next) 30 { 31 int v=edge[i].to; 32 if(d[v]) continue; 33 d[v]=d[u]+1; 34 f[v][0]=u; 35 for(int j=1;j<=t;j++) 36 f[v][j]=f[f[v][j-1]][j-1]; 37 q.push(v); 38 } 39 } 40 } 41 42 int lca(int x,int y) 43 { 44 if(d[x]>d[y]) swap(x,y); 45 for(int i=t;i>=0;i--) 46 if(d[f[y][i]]>=d[x]) y=f[y][i]; 47 if(x==y) return x; 48 for(int i=t;i>=0;i--) 49 if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; 50 return f[x][0]; 51 } 52 53 void review(int u,int fa) 54 { 55 for(int i=head[u];i;i=edge[i].next) 56 { 57 int v=edge[i].to; 58 if(v==fa) continue; 59 review(v,u); 60 val[u]+=val[v]; 61 } 62 } 63 64 int main() 65 { 66 scanf("%d%d",&n,&m); 67 t=log2(n)+1; 68 for(int i=1;i<=n-1;i++) 69 { 70 int x=0,y=0; 71 scanf("%d%d",&x,&y); 72 add(x,y);add(y,x); 73 } 74 init(); 75 for(int i=1;i<=m;i++) 76 { 77 int x=0,y=0; 78 scanf("%d%d",&x,&y); 79 int fa=lca(x,y); 80 val[x]++,val[y]++; 81 val[fa]-=2; 82 } 83 review(1,0); 84 for(int i=2;i<=n;i++) 85 { 86 if(val[i]==0) ans+=m; 87 else if(val[i]==1) ans++; 88 } 89 printf("%d ",ans); 90 return 0; 91 }