您的位置:首页 > 其它

oldssoj2676B(环)

2015-08-27 11:46 190 查看

题目描述

JRY is the leader of a village. He has n lands, and there are n roads connecting them. There is at most one road connecting two lands and all lands are connected.

Now, JRY wants to divided the n lands into k disjoint sets of equal size, satisfying that one can move between any two lands belonging to the same set passing only through lands frome this set.

Furthermore, he wants to know how many k(1≤k≤n) he can choose.

输入

There are multiple testcases, the sum of n is less then 106.

For each test case, the first line contains one integer n(1≤n≤105).

The next line contains n integers, the i-th integer ai means that there is an edge between i and ai. It is guaranteed that the graph doesn't contain self loops and multiple edges.

输出

For each testcase print a single integer - the number of ways to choose the integer k.

样例输入

62 3 4 5 6 162 4 2 3 4 3

样例输出

43

提示

Hint

Case 1 : $k$ = 1,2,3,6

Case 2: $k$ = 1,3,6

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=200005;
struct data{
int v,next;
}e[maxn*2];
int n,fst[maxn],huan[maxn],fa[maxn],tot,dep[maxn],sum[maxn],ans,Set,pres[maxn],num[maxn],cnt;
bool vis[maxn];
inline int get(){
char c;while(!isdigit(c=getchar()));
int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
return v;
}
inline void add(int x,int y){
e[++cnt].v=y;e[cnt].next=fst[x],fst[x]=cnt;
}
inline void gethuan(int x){
for(int i=fst[x];i;i=e[i].next){
int y=e[i].v;
if(fa[x]==y)continue;
if(!dep[y]){
dep[y]=dep[x]+1;
fa[y]=x;
gethuan(y);
}
else if(dep[y]<dep[x]){
int t=x;
while(t!=y){
huan[++tot]=t;
vis[t]=1;
t=fa[t];
}
huan[++tot]=t;vis[t]=1;
}
}
}
inline void getsum(int x,int lim){
sum[x]=1;
for(int i=fst[x];i;i=e[i].next){
int y=e[i].v;
if(y!=lim && !vis[y]){
getsum(y,x);
sum[x]+=sum[y];
}
}
}
int main(){
while(scanf("%d",&n)==1){
memset(fst,0,sizeof(fst));
memset(e,0,sizeof(e));
memset(vis,0,sizeof(vis));
memset(sum,0,sizeof(sum));
memset(dep,0,sizeof(dep));
ans=2;tot=0;Set=0;cnt=0;
for(int i=1;i<=n;++i){
int x=get();fa[i]=i;
add(i,x);add(x,i);
}
dep[1]=1;gethuan(1);
for(int i=1;i<=tot;++i)getsum(huan[i],huan[i]);
for(int i=2;i<n;++i){
if(n%i)continue;
int p=i,k=n/p;Set=0;
memset(num,0,sizeof(num));
for(int j=1;j<=n;++j)if(!vis[j] && !(sum[j]%p))++Set;
pres[0]=0;
for(int j=1;j<=tot;++j){        //删掉环上huan【1】和huan【tot】的边
int q=huan[j];
pres[j]=(pres[j-1]+sum[q])%p;
++num[pres[j]];
}
for(int j=1;j<=tot;++j)if(Set+num[pres[j]]==k){  //枚举环上的边
++ans;
break;
}
}
printf("%d\n",ans);
}
return 0;
}


思想:n个点,n条边,可以看成一棵树,树上有一个环,先找出环上点的子数能分成几份,之后就与环上点没什么关系了。再枚举环上的边,拉成一棵树,再算环上能分成几份,若是份数之和与k相等,++ans。

num[ res ]数组记录余数为res的数目。容易发现,环上那个末尾节点的num=0,并且枚举下一条边时,num都减去同一个数,不用处理,即只要num=num[末尾节点],余数为零,份数加1。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: