【NOIP模拟】树上摩托

Description

Sherco是一位经验丰富的魔♂法师。
Sherco在第零次圣杯战争中取得了胜利,并取得了王之宝藏——王の树。
他想把这棵树砍去任意条边,拆成若干棵新树,并装饰在他的摩托上,让他的摩托更加酷炫。
但Sherco认为,这样生成的树不具有美感,于是Sherco想让每棵新树的节点数相同。
他想知道有多少种方法分割这棵树。

Solution

这题一看上去就很难的样子,一直找到所有的约数之后,然后在逐个的找有多少种情况,搞的最后只能打40分的暴搜。
其实有两个很重要的性质:
1、分割完的每个树的节点个数都是n的约数(这个很显然)
2、对于每种约数的分割方案只有一种(感性的证明一下,如果在x点之上的边割掉了,x点及以下的所有点是k的倍数,x以上的所有点是k的倍数,必须要在加进k个点才能改变,但是那样是早已被割出来了的)
有了这个东西之后,我们枚举k,然后找到所有子节点的个数是k的倍数的节点,然后统计起来,如果数量等于 nk ,那么答案就加1。
怎么找这些点呢,用bz[i]表示节点数为i的点的个数。

注意会爆栈

dfs找儿子的节点个数要改成用bfs来找。

Code

#include 
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=1000007;
int i,j,k,l,t,n,m,ans;
int first[maxn*2],last[maxn*2],next[maxn*2],num;
int size[maxn*2];
int bz[maxn*2];
int data[maxn*2],f[maxn*2],tuo[maxn*2];
bool az[maxn];
void add(int x,int y){
    last[++num]=y,next[num]=first[x],first[x]=num;
}
void bfs(int x){
    int i,head=0,tail=1,now;
    data[1]=x;az[1]=1;
    while(headif(!az[last[i]]){
                az[last[i]]=1;
                f[last[i]]=now;
                data[++tail]=last[i];
            }
        }
    }
    memset(az,0,sizeof(az));
    fo(i,1,tail){
        if(!az[data[i]]){
            az[data[i]]=1;
            tuo[++tuo[0]]=data[i];
        }
    }
}
int main(){
    scanf("%d",&n);
    fo(i,1,n-1){
        scanf("%d%d",&k,&l);
        add(k,l),add(l,k);
    }
    bfs(1);
    fod(i,n,1){
        size[tuo[i]]+=1;
        size[f[tuo[i]]]+=size[tuo[i]];
        bz[size[tuo[i]]]+=1;
    }
    fo(k,1,n){
        if(n%k==0){
             i=k,j=0;
             while(i<=n){
                 j+=bz[i];
                 i+=k;
             }
             if(j==n/k)ans++;
        }
    }
    printf("%d\n",ans);
}

你可能感兴趣的:(noip,树)