http://acm.ocrosoft.com/problem.php?cid=2184&pid=0
给定一颗最小生成树,然后再给m条的新边,求删去一条树边和新边后有多少种办法使图分成独立的两个连通块。
每给定一条新边,必定会出现一个环。
假定对于某点,只有一条树边,断开这一条树边,则
1、对于该点,若没有新边,即不存在环,任意的,可以删去图中的任意一条新边,方法数+m;
2、对于该点,若只有一条新边,即只存在一个环,可以删去这一条新边,方法数+1;
若对于某点,边数(新边+树边)大于等于三,即经过该点有两个以上环,则不能通过断去两条边的方式获得两个独立的连通块。
当添加一条新边 u,v 会形成一个环,那么环上各点经过环的数量都加一,所以需要多次修改。
采用边差分的方式记录并不断修改经过该点的环的数量 ,此时需要LCA来帮助确定差分作用的区间。
在新边读入完成后,还原差分得到最后的图,按上述方式计算得到方法数。
1 #include<cstdio> 2 #include<cmath> 3 #include<iostream> 4 #include<cstring> 5 #include<algorithm> 6 #include<stack> 7 #include<queue> 8 #include<map> 9 #include<vector> 10 #include<set> 11 #include<iomanip> 12 #define inf 0x3f3f3f3f 13 #define ll long long 14 #define debug printf("yes\n"); 15 using namespace std; 16 int n,m,tot,x,y,p,ans,lg[100010],diff[100010<<1],dep[100010],to[100010<<2],nxt[100010<<2],head[100010<<2],fa[100010][20]; 17 int read(){ 18 int x=0,f=1;char ch=getchar(); 19 while(ch<'0'||ch>'9'){ 20 if(ch=='-')f=-1; 21 ch=getchar(); 22 } 23 while(ch>='0'&&ch<='9'){ 24 x=x*10+ch-'0'; 25 ch=getchar(); 26 } 27 return x*f; 28 } 29 void add(int x,int y){ 30 to[++tot]=y; 31 nxt[tot]=head[x]; 32 head[x]=tot; 33 } 34 void dfs_lca(int x,int father){ 35 fa[x][0]=father;dep[x]=dep[father]+1; 36 for(int i=1;i<=lg[dep[x]];++i) 37 fa[x][i]=fa[fa[x][i-1]][i-1];//状态转移计算出各种2^i跳法到达的位置 38 for(int i=head[x];i;i=nxt[i]) 39 if(to[i]!=father)dfs_lca(to[i],x); 40 } 41 int lca(int x,int y){ 42 if(dep[x]<dep[y]) swap(x,y); 43 while(dep[x]>dep[y]) 44 x=fa[x][lg[dep[x]-dep[y]]-1]; 45 if(x==y) return x; 46 for(int k=lg[dep[x]]-1;k>=0;--k) 47 if(fa[x][k]!=fa[y][k]) 48 x=fa[x][k],y=fa[y][k]; 49 return fa[x][0]; 50 } 51 void dfs_cf(int u,int father){ 52 for(int i=head[u];i;i=nxt[i]){ 53 if(to[i]==father) continue; 54 dfs_cf(to[i],u); 55 diff[u]+=diff[to[i]]; 56 } 57 } 58 int main(){ 59 n=read();m=read(); 60 for(int i=1;i<=n;++i) 61 lg[i]=lg[i-1]+(1<<lg[i-1]==i); //预处理各深度log2 62 for(int i=1;i<n;++i){ 63 x=read();y=read(); 64 add(x,y);add(y,x);//构建最小树的边 65 } 66 dfs_lca(1,0); 67 for(int i=1;i<=m;++i){ 68 x=read();y=read(); 69 p=lca(x,y); 70 ++diff[x];++diff[y];diff[p]-=2; //边差分,记录环 71 } 72 dfs_cf(1,0);//还原为经过该点的环的数量 73 for(int i=2;i<=n;++i) 74 if(diff[i]==0) ans+=m; 75 else if(diff[i]==1) ans++; 76 printf("%d",ans); 77 return 0; 78 }