LOJ 暗的连锁 (POJ3417)【LCA + 树上差分 + 计数】

LOJ 暗的连锁

蛮不错的题。。。

题目大意:先切一条主要边,再切一条附加边,使得图不连通的方案数。

首先抓住一个要点:两个点之间加了一条附加边,等价于两点的唯一路径上所有点之间有两条路可互达。

这是一条非常重要的性质。这样我们就可以利用 LCA (这里我用倍增,你们用 RMQ 也行)求出每条边被覆盖了几次。

这里又用到了另一个技巧:树上差分

具体的话:搜博客吧。。。

和线性差分思想一样,求子树的值 + 自身的值,即为该节点的值。

修改的时候:c[u]++, c[v]++, c[lca(u,v)]-=2 (至于为什么是这样,画画图,理解理解)

1、若一次都未被覆盖,则切断这条边一定成立,则它可以和任意一条附加边 (m条) 成为组合,即对答案的贡献为 m 。

2、若覆盖了一次,那么只有唯一的一种解就是把这条边切两遍,即对答案的贡献为 1 。

扫描二维码关注公众号,回复: 3679631 查看本文章

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\n",ans);
73     return 0;
74 }
View Code

fighting fighting fighting

猜你喜欢

转载自www.cnblogs.com/Frank-King/p/9832660.html