poj 3417 Network
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 5276 | Accepted: 1512 |
Description
Yixght is a manager of the company called SzqNetwork(SN). Now she's very worried because she has just received a bad news which denotes that DxtNetwork(DN), the SN's business rival, intents to attack the network of SN. More unfortunately, the original network of SN is so weak that we can just treat it as a tree. Formally, there are N nodes in SN's network, N-1 bidirectional channels to connect the nodes, and there always exists a route from any node to another. In order to protect the network from the attack, Yixght builds M new bidirectional channels between some of the nodes.
As the DN's best hacker, you can exactly destory two channels, one in the original network and the other among the M new channels. Now your higher-up wants to know how many ways you can divide the network of SN into at least two parts.
Input
The first line of the input file contains two integers: N (1 ≤ N ≤ 100 000), M (1 ≤ M ≤ 100 000) — the number of the nodes and the number of the new channels.
Following N-1 lines represent the channels in the original network of SN, each pair (a,b) denote that there is a channel between node a and node b.
Following M lines represent the new channels in the network, each pair (a,b) denote that a new channel between node a and node b is added to the network of SN.
Output
Output a single integer — the number of ways to divide the network into at least two parts.
Sample Input
4 1
1 2
2 3
1 4
3 4
Sample Output
3
Source
题意:先给出一棵无根树,然后下面再给出m条边,把这m条边连上,然后每次你能毁掉两条边,规定一条是树边,一条是新边,问有多少种方案能使树断裂。
翻译:LCA 我们知道,这m条边连上后这颗树必将成环,假设新边为(u,v),那么环为u---->LCA(u,v)------->v-------->u,我们给这个环上的边计数1,表示这些边被一个环覆盖了一次。添加了多条新边后,可知树上有些边是会被多次覆盖的,画图很容易发现,但一个树边被覆盖了2次或以上,它就是一条牢固的边,就是说毁掉它再毁掉任何一条新边都好,树都不会断裂,这个结论也是很容易证明的,画图更明显,所以不累述
所以这启发了我们,要统计所有的边被覆盖了几次,我们分情况来讨论
1.覆盖0次,说明这条边不在任何一个环上,这样的边最脆弱,单单是毁掉它就已经可以使树断裂了,这时候只要任意选一条新边去毁,树还是断裂的,所以这样的树边,就产生m种方案(m为新边条数)
2.覆盖1次,说明这条边在一个环上,且,仅在一个环上,那么要使树断裂,就毁掉这条树边,并且毁掉和它对应的那条新边(毁其他的新边无效),就一定能使树断裂,这种树边能产生的方案数为1,一条这样的树边只有唯一解
3.覆盖2次或以上,无论怎么样都不能使树断裂,产生的方案数为0
所以,如果我们能知道所有的树边的覆盖,那么统计一次就行了,所以问题只剩下,怎么每条边被覆盖了几次?
需要用到树DP。
首先我们定义dp[u]的意义为,u所对应的那条父边(u和它父亲连接的那条边)被覆盖的次数
对应一条新边(u,v),我们知道是要求LCA(u,v)的,这时候我们计数dp[u]++ , dp[v]++ , dp[lca]-=2
为什么这样计数?我们试着看看,点u和点v和点lca,都试着沿路径一直回到树根处(注意不是回到LCA而是树根),u的路径中每经过一个点,就将这些点上的值加上dp[u],同样v的路径上没经过一个点就将这些点上的值加上dp[v],lca也是这样。你会发现,lca回到树根的部分,其实被抵消掉了,dp值没有变化,而u到lca,v到lca部分的值都已经分别加上了dp[u],dp[v]
这启发了我们,我们在求完所有m对顶点的LCA后,每个u和v都做一次dp[u]++,dp[v]++,dp[lca]-=2,然后我从树根开始向下遍历一次整棵树,在回溯的时候就执行累加dp[u],dp[v]的操作
这其实就是树DP的过程,好像说得有点复杂了,不过其实不难理解
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MAXN 100005 using namespace std; int n,q,tot,tot1; int to[MAXN*2],net[MAXN*2],head[MAXN*2]; int to1[MAXN*2],net1[MAXN*2],head1[MAXN*2]; int dp[MAXN],fa[MAXN],vis[MAXN],lca[MAXN*2]; void add(int u,int v){ to[tot]=v;net[tot]=head[u];head[u]=tot++; to[tot]=u;net[tot]=head[v];head[v]=tot++; } void add1(int u,int v){ to1[tot1]=v;net1[tot1]=head1[u];lca[tot1]=-1;head1[u]=tot1++; to1[tot1]=u;net1[tot1]=head1[v];lca[tot1]=-1;head1[v]=tot1++; } int find(int x){ if(fa[x]==x) return x; else return fa[x]=find(fa[x]); } void tarjin(int now){ vis[now]=1; for(int i=head1[now];i!=-1;i=net1[i]) if(vis[to1[i]]){ int LCA=find(to1[i]); lca[i]=lca[i^1]=LCA; } for(int i=head[now];i!=-1;i=net[i]) if(!vis[to[i]]){ tarjin(to[i]); fa[to[i]]=now; } } void dfs(int now){ vis[now]=1; for(int i=head[now];i!=-1;i=net[i]) if(!vis[to[i]]){ dfs(to[i]); dp[now]+=dp[to[i]]; } } int main(){ scanf("%d%d",&n,&q); memset(head,-1,sizeof(head)); memset(head1,-1,sizeof(head1)); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); add(u,v); } for(int i=0;i<q;i++){ int u,v; scanf("%d%d",&u,&v); add1(u,v); dp[u]++;dp[v]++; } tarjin(1); for(int i=0;i<q;i++) dp[lca[i*2]]-=2; memset(vis,0,sizeof(vis)); dfs(1); int ans=0; for(int i=2;i<=n;i++) if(dp[i]==1) ans++; else if(dp[i]==0) ans+=q; cout<<ans<<endl; }