SEERC 2017 L Divide and Conquer
题面
题意
给出由A,B两棵树重合后组成的一张图,问至少删掉几条边才能将图分成两块。
做法
首先,这张图一共有个点,条边,因此度数最小的点的度数一定小于等于3,这就说明了答案一定为2或3,这也就说明了A,B两棵树中一定有一棵树种只删了一条边,因此我们可以暴力枚举在A树中删掉的是哪条边,然后统计B树中有几条边连接着A树删掉这条边后的两个联通块,而这个可以用树上差分来解决:
对于B树中的每一条边,令.
这样A中每个点的子树和即为删去这个点和它父亲的连边之后两个联通块间B的边数。
因为如果,在同一联通块中,则它们的也一定在同一联通块,不会对答案做出贡献,反之必然会对答案做出1的贡献。
代码
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#define P pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define N 100100
using namespace std;
int n,ans[5],num[N],in[N],out[N],fa[N],tt;
bool vis[N];
P ba[N],bb[N];
struct Sz
{
int sz[N];
inline int lb(int u){return u&(-u);}
inline void add(int u,int v){for(;u<=n;u+=lb(u)) sz[u]+=v;}
inline int as(int u){int res=0;for(;u;u-=lb(u)) res+=sz[u];return res;}
inline void clear(){memset(sz,0,sizeof(sz));}
inline int ask(int u){return as(out[u])-as(in[u]-1)+1;}
};
Sz sz;
vector<int>son[N],que[N];
int ff(int u){return u==fa[u]?u:fa[u]=ff(fa[u]);}
void dfs(int now,int last)
{
in[now]=++tt;
int i,t;
for(i=0;i<son[now].size();i++)
{
t=son[now][i];
if(t==last) continue;
dfs(t,now);
}
for(i=0;i<que[now].size();i++) if(vis[que[now][i]]) num[ff(que[now][i])]-=2;
if(last!=-1) fa[ff(now)]=ff(last);
out[now]=tt;
vis[now]=1;
}
int main()
{
int i,j,t;
cin>>n;
for(i=1;i<n;i++)
{
scanf("%d%d",&ba[i].fi,&ba[i].se);
son[ba[i].fi].push_back(ba[i].se);
son[ba[i].se].push_back(ba[i].fi);
}
for(i=1;i<n;i++)
{
scanf("%d%d",&bb[i].fi,&bb[i].se);
que[bb[i].fi].push_back(bb[i].se);
que[bb[i].se].push_back(bb[i].fi);
num[bb[i].fi]++,num[bb[i].se]++;
}
for(i=1;i<=n;i++) fa[i]=i;
dfs(1,-1);
for(i=1;i<=n;i++) sz.add(in[i],num[i]);
for(i=2;i<=n;i++)
{
t=sz.ask(i);
if(t<=3) ans[t]++;
}
if(ans[2])
{
cout<<2<<" "<<ans[2];
return 0;
}
memset(num,0,sizeof(num));
memset(vis,0,sizeof(vis));
for(i=1;i<=n;i++)
{
fa[i]=i;
son[i].clear();
que[i].clear();
sz.clear();
}
tt=0;
for(i=1;i<n;i++)
{
son[bb[i].fi].push_back(bb[i].se);
son[bb[i].se].push_back(bb[i].fi);
}
for(i=1;i<n;i++)
{
que[ba[i].fi].push_back(ba[i].se);
que[ba[i].se].push_back(ba[i].fi);
num[ba[i].fi]++,num[ba[i].se]++;
}
dfs(1,-1);
for(i=1;i<=n;i++) sz.add(in[i],num[i]);
for(i=2;i<=n;i++)
{
t=sz.ask(i);
if(t<=3) ans[t]++;
}
cout<<3<<' '<<ans[3];
return 0;
}