LOJ 暗的连锁
蛮不错的题。。。
题目大意:先切一条主要边,再切一条附加边,使得图不连通的方案数。
首先抓住一个要点:两个点之间加了一条附加边,等价于两点的唯一路径上所有点之间有两条路可互达。
这是一条非常重要的性质。这样我们就可以利用 LCA (这里我用倍增,你们用 RMQ 也行)求出每条边被覆盖了几次。
这里又用到了另一个技巧:树上差分。
具体的话:搜博客吧。。。
和线性差分思想一样,求子树的值 + 自身的值,即为该节点的值。
修改的时候:c[u]++, c[v]++, c[lca(u,v)]-=2 (至于为什么是这样,画画图,理解理解)
1、若一次都未被覆盖,则切断这条边一定成立,则它可以和任意一条附加边 (m条) 成为组合,即对答案的贡献为 m 。
2、若覆盖了一次,那么只有唯一的一种解就是把这条边切两遍,即对答案的贡献为 1 。
3、其余的情况均不成立,即对答案无贡献。
贴代码:
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int N=1e5+5; 5 int n,m,nxt[N*2],to[N*2],hea[N],tot,c[N],f[N][25],dep[N],num[N]; 6 bool vis[N]; 7 ll ans; 8 inline int read() 9 { 10 int x=0,f=1; char ch=getchar(); 11 while (!isdigit(ch)) f=(ch=='-')?-f:f,ch=getchar(); 12 while (isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); 13 return x*f; 14 } 15 inline void add(int x,int y) 16 { 17 to[++tot]=y; nxt[tot]=hea[x]; hea[x]=tot; 18 } 19 inline void dfs(int x,int d,int fa) 20 { 21 for (int i=hea[x]; i; i=nxt[i]) 22 { 23 int y=to[i]; 24 if (y==fa || dep[y]) continue; 25 f[y][0]=x; dep[y]=d+1; dfs(y,d+1,x); 26 } 27 } 28 inline int lca(int x,int y) 29 { 30 if (dep[x]<dep[y]) swap(x,y); 31 for (int i=20; ~i; --i) if (dep[f[x][i]]>=dep[y]) x=f[x][i]; 32 if (x==y) return x; 33 for (int i=20; ~i; --i) 34 if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; 35 return f[x][0]; 36 } 37 inline int dfs1(int x) 38 { 39 vis[x]=1; num[x]=c[x]; 40 for (int i=hea[x]; i; i=nxt[i]) 41 { 42 int y=to[i]; 43 if (vis[y]) continue; 44 num[x]+=dfs1(y); 45 } 46 return num[x]; 47 } 48 int main() 49 { 50 n=read(),m=read(); 51 register int u,v; 52 for (int i=1; i<n; ++i) 53 { 54 u=read(),v=read(); 55 add(u,v); add(v,u); 56 } 57 dfs(1,0,0); 58 for (int i=1; i<=20; ++i) 59 for (int j=1; j<=n; ++j) 60 f[j][i]=f[f[j][i-1]][i-1]; 61 for (int i=1; i<=m; ++i) 62 { 63 u=read(),v=read(); 64 c[u]++; c[v]++; c[lca(u,v)]-=2; 65 } 66 dfs1(1); ans=0; 67 for (int i=1; i<=n; ++i) 68 { 69 if (num[i]==0 && i!=1) ans+=m; 70 else if (num[i]==1) ans++; 71 } 72 printf("%lld ",ans); 73 return 0; 74 }
fighting fighting fighting