Gym101669L Divide and Conquer

Link
首先答案显然不可能是\(1\)
然后有一个很显然的方法是把度数最小的点的所有边删掉,注意到总的度数为\(4n-4\),所以一定存在某个点度数不大于\(3\),因此答案不大于\(3\)
那么可行的答案就只有\(2,3\)
也就是说要么两棵树各割掉一条边,要么一棵树割一条边另一棵树割两条边。
我们枚举割掉第一棵树上的哪条边,然后计算在另一棵树上分开这两个连通块所需要割掉的最小边数,这个可以用差分进行维护。
注意如果求出来的答案为\(3\),那么我们还需要再在另一棵树上再枚举一遍。

#include<cstdio>
#include<cctype>
#include<vector>
const int N=300007;
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
int n,ans,cnt;
struct Tree
{
    std::vector<int>e[N];int col[N],size[N],son[N];
    void dfs(int u,int fa){size[u]=1;for(int v:e[u])if(v^fa)if(dfs(v,u),size[u]+=size[v],size[v]>size[son[u]])son[u]=v;}
    void build(){for(int i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);dfs(1,0);}
    int color(int u){int tot=0;col[u]=1;for(int v:e[u])tot+=col[v]?-1:1;return tot;}
    void reset(int u,int fa,Tree*t){t->col[u]=0;for(int v:e[u])if(v^fa)reset(v,u,t);}
    int color(int u,int fa,Tree*t){int tot=t->color(u);for(int v:e[u])if(v^fa)tot+=color(v,u,t);return tot;}
    int cal(int u,int fa,Tree*t)
    {
	if(!u) return 0;
	for(int v:e[u]) if(v^fa&&v^son[u]) cal(v,u,t),reset(v,u,t);
	int tot=cal(son[u],u,t)+t->color(u)+1;
	for(int v:e[u]) if(v^fa&&v^son[u]) tot+=color(v,u,t);
	if(u^1) ans>tot? ans=tot,cnt=1:cnt+=ans==tot;
	return tot-1;
    }
}t1,t2;
int main()
{
    n=read(),ans=3,t1.build(),t2.build(),t1.cal(1,0,&t2);
    if(ans==3) t2.cal(1,0,&t1);
    printf("%d %d",ans,cnt);
}
posted @ 2020-03-26 18:20  Shiina_Mashiro  阅读(154)  评论(0编辑  收藏  举报