【题目描述】
原题来自:POJ 3417
Dark 是一张无向图,图中有 NN 个节点和两类边,一类边被称为主要边,而另一类被称为附加边。Dark 有 N–1N–1 条主要边,并且 Dark 的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark 还有 MM 条附加边。
你的任务是把 Dark 斩为不连通的两部分。一开始 Dark 的附加边都处于无敌状态,你只能选择一条主要边切断。一旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切断。但是你的能力只能再切断 Dark 的一条附加边。
现在你想要知道,一共有多少种方案可以击败 Dark。注意,就算你第一步切断主要边之后就已经把 Dark 斩为两截,你也需要切断一条附加边才算击败了 Dark。
【输入】
第一行包含两个整数 NN 和 MM;
之后 N–1N–1行,每行包括两个整数 AA 和 BB,表示 AA 和 BB 之间有一条主要边;
之后 MM 行以同样的格式给出附加边。
【输出】
输出一个整数表示答案。
【输入样例】
4 1 1 2 2 3 1 4 3 4
【输出样例】
3
【提示】
数据范围与提示:
对于 20% 的数据,1≤N,M≤1001≤N,M≤100;
对于 100% 的数据,1≤N≤105,1≤M≤2×1051≤N≤105,1≤M≤2×105 。数据保证答案不超过 231−1231−1。
【算法分析】
根据题意,“主要边”构成一棵树,“附加边”则是“非树边”。把一条附加边(x,y)添加到主要边构成的树中,会与树上x,y之间的路径一起形成一个环。如果第一步选择切断x,y之间主要边构成路径上的某条边,那么第二步就必须切断附加边(x,y),才能令Dark被斩为不连通的两部分。 因此,我们称每条附加边(x,y)都把树上x,y之间的路径上的每条边“覆盖了一次”。我们只需统计出每条“主要边”被覆盖了多少次。
若第一步把被覆盖0次的主要边切断,则第二步可任意切断一条附加边。
若第一步把被覆盖1次的主要边切断,则第二步方法唯一。
若第一步把被覆盖2次及2次以上的主要边切断,则第二步无论如何操作都不能击败Dark。
这样我们就得到了击败Dark的方案数。
综上所述,下面我们要解决的问题模型是:给定一张无向图和一棵生成树,求每条“树边”被“非树边”覆盖了多少次。下图左侧的例子中标记了覆盖次数,虚线为“非树边”。
解决此问题有一个经典做法,我们称之为“树上差分算法”。树上差分算法与差分序列的思想类似。
对应在树上,我们给每个节点一个初始为0的权值,然后对每条非树边(x,y),令节点x的权值加1,节点y的权值加1,节点LCA(x,y)的权值减2,如上图右侧的例子所示。
最后对这棵生成树进行一次深度优先遍历,求出F[x]表示以x为根的子树中各节点的权值之和。F[x]就是x与它的父节点之间的“树边”被覆盖的次数。时间复杂度O(N+M)。
我是用树剖做的哟,如果对树剖还不太了解,可以点开下面这个链接看下鸭☟☟☟
这是一个链接(人家才不是莫得感情的链接呢)
最后送上可复制AC代码(亲测有效(*๓´╰╯`๓))
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1e6+5; 4 int next[2*N],head[N],to[N*2],d[N],add[N],fa[N],sum[N],top[N],size[N],son[N]; 5 int n; 6 int m,q,w,ans,p,tot; 7 int read() 8 { 9 int f=1;char ch; 10 while((ch=getchar())<'0'||ch>'9') 11 if(ch=='-')f=-1; 12 int res=ch-'0'; 13 while((ch=getchar())>='0'&&ch<='9') 14 res=res*10+ch-'0'; 15 return res*f; 16 } 17 void write(int x) 18 { 19 if(x<0) 20 { 21 putchar('-'); 22 x=-x; 23 } 24 if(x>9)write(x/10); 25 putchar(x%10+'0'); 26 } 27 void ADD(int x,int y) 28 { 29 next[++tot]=head[x]; 30 head[x]=tot; 31 to[tot]=y; 32 } 33 int lca(int x,int y) 34 { 35 while(top[x]!=top[y]) 36 d[top[x]]>d[top[y]]?x=fa[top[x]]:y=fa[top[y]]; 37 return d[x]<d[y]?x:y; 38 } 39 void dfs1(int s) 40 { 41 size[s]=1; 42 for(int i=head[s],t;i,t=to[i];i=next[i]) 43 { 44 if(t==fa[s])continue; 45 fa[t]=s;d[t]=d[s]+1; 46 dfs1(t); 47 if(size[t]>size[son[s]])son[s]=t; 48 size[s]+=size[t]; 49 50 } 51 } 52 void dfs2(int s) 53 { 54 if(son[s]) 55 { 56 top[son[s]]=top[s]; 57 dfs2(son[s]); 58 } 59 for(int i=head[s],t;i,t=to[i];i=next[i]) 60 { 61 if(!top[t]) 62 { 63 top[t]=t; 64 dfs2(t); 65 } 66 } 67 } 68 void dfs3(int s) 69 { 70 sum[s]=add[s]; 71 for(int i=head[s],t;i,t=to[i];i=next[i]) 72 { 73 if(t!=fa[s]) 74 { 75 dfs3(t); 76 sum[s]+=sum[t]; 77 } 78 } 79 } 80 int main() 81 { 82 n=read();m=read(); 83 for(int i=1;i<n;i++) 84 { 85 int x,y; 86 x=read();y=read(); 87 ADD(x,y); 88 ADD(y,x); 89 } 90 top[1]=d[1]=1; 91 dfs1(1); 92 93 dfs2(1); 94 for(int i=1;i<=m;i++) 95 { 96 int x,y; 97 x=read();y=read(); 98 add[x]++;add[y]++;add[lca(x,y)]-=2; 99 } 100 dfs3(1); 101 for(int i=2;i<=n;i++) 102 { 103 if(sum[i]<2)sum[i]?ans++:ans+=m; 104 } 105 write(ans); 106 return 0; 107 }