题意:

求一共有多少种方案可以把一棵树分成大小相同的几块。(即切掉一些边,使得每个连通块的点数相同)

分析:

枚举每一个数的因子,然后遍历整棵树,看是否可以划分,如果可以的话,ans+1(因为可以发现只有一种划分方法)。

那么在遍历的时候怎么判断呢?

从上向下求一遍siz,之后每一次检查now这个大小是否可以被划分时,再用dfs2从上向下dfs一遍,在回溯时将大于now的尽量减去。

具体减的过程:

当一个点的儿子v,siz[v]是大于now的话,说明减不掉了,即其儿子以下不能划分成大小相同的块,直接打标记一直返回。

刚好等于,就剪掉儿子,并且改变u的siz大小(通过返回值传给深度较浅的节点)。

小于,则说明可以积累到下一次再减,就不用管

 

#include<bits/stdc++.h>
using namespace std;
#define N 1000005
int tot=0,siz[N],to[N<<1],nex[N<<1],head[N],now,n,fl=0,tmp[N],ans=0;
void add(int a,int b){ to[++tot]=b; nex[tot]=head[a]; head[a]=tot; }
void dfs1(int u,int fa)
{
    siz[u]=1;
    for(int i=head[u];i;i=nex[i]){
        int v=to[i];
        if(v==fa) continue;
        dfs1(v,u);
        siz[u]+=siz[v];
    }
}
int dfs2(int u,int fa)
{
    if(fl) return 0;
    int flagg=0;
    tmp[u]=siz[u];
    for(int i=head[u];i;i=nex[i]){
        int v=to[i];
        if(v==fa) continue;
        int x=dfs2(v,u);
        if(fl) return 0;
        tmp[u]-=x;
        flagg+=x;
        if(tmp[v]==now) flagg+=now,tmp[v]=0,tmp[u]-=now;
        else if(tmp[v]>now) fl=1;
    }
    return flagg;
}
void solve(int x)
{
    fl=0; now=x;
    dfs2(1,0);
    if(!fl) ans++;
}
int main()
{
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    int a,b;
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++) scanf("%d%d",&a,&b),add(a,b),add(b,a);
    dfs1(1,0);
    int xx=sqrt(n);
    for(int i=1;i<=xx;i++)
    if(n%i==0){
        solve(i);
        if(n/i!=i) solve(n/i);
    } 
    printf("%d\n",ans);
}
/*
6
1 2
2 3
2 4
4 5
5 6
ans 3
8
1 2 
1 7 
1 8
2 3
2 4
3 5
3 6
ans 2
8
1 2 
1 7 
2 3
2 4
3 5
3 6
5 9
ans 4
*/

 

posted on 2019-08-22 19:41  rua-rua-rua  阅读(255)  评论(0编辑  收藏  举报