题意:
求一共有多少种方案可以把一棵树分成大小相同的几块。(即切掉一些边,使得每个连通块的点数相同)
分析:
枚举每一个数的因子,然后遍历整棵树,看是否可以划分,如果可以的话,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 ",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 */