【LOJ 10131】暗的连锁
题目描述:
Dark 是一张无向图,图中有 N 个节点和两类边,一类边被称为主要边,而另一类被称为附加边。Dark 有 N–1 条主要边,并且 Dark 的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark 还有 M 条附加边。
你的任务是把 Dark 斩为不连通的两部分。一开始 Dark 的附加边都处于无敌状态,你只能选择一条主要边切断。一旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切断。但是你的能力只能再切断 Dark 的一条附加边。
现在你想要知道,一共有多少种方案可以击败 Dark。注意,就算你第一步切断主要边之后就已经把 Dark 斩为两截,你也需要切断一条附加边才算击败了 Dark。
输入格式:
第一行包含两个整数 N 和 M;
之后 N–1 行,每行包括两个整数 A 和 B,表示 A 和 B 之间有一条主要边;
之后 M 行以同样的格式给出附加边。
输出格式:
输出一个整数表示答案。
输入样例
4 1
1 2
2 3
1 4
3 4
输出样例
3
题解:详见一本通p251。主要就是用到了树上差分算法啦。
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #include<cstdlib> #include<algorithm> #include<queue> #include<bits/stdc++.h> using namespace std; const int N=100002; struct node{ int next; int to; }e[N*4]; int head[N],d[N],x,y; int f[N][22],cnt,ans[N]; int n,m; void dfs_YCLL(int u,int fa){ d[u]=d[fa]+1; for(int i=0;i<=19;i++) f[u][i+1]=f[f[u][i]][i]; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v==fa) continue; f[v][0]=u; dfs_YCLL(v,u); } } int lca(int x,int y){ if(d[x]<d[y]) swap(x,y); for(int i=20;i>=0;i--){ if(d[f[x][i]]>=d[y]) x=f[x][i]; if(x==y) return x; } for(int i=20;i>=0;i--) if(f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } return f[x][0]; } void dfs(int x){ for(int i=head[x];i;i=e[i].next){ int v=e[i].to; if(v==f[x][0]) continue; dfs(v); ans[x]+=ans[v]; } } void add(int x,int y){ e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt; } int main(){ freopen("10131.in","r",stdin); freopen("10131.out","w",stdout); scanf("%d %d",&n,&m); for(int i=1;i<n;i++){ scanf("%d %d",&x,&y); add(x,y); add(y,x); } dfs_YCLL(1,0); for(int i=1;i<=m;i++){ scanf("%d %d",&x,&y); ans[x]++; ans[y]++; int z=lca(x,y); ans[z]-=2; } dfs(1); int yc=0; for(int i=2;i<=n;i++){ if(ans[i]==1) yc++; if(ans[i]==0) yc+=m; } printf("%d\n",yc); return 0; }