oldssoj2676B(环)

1 篇文章 0 订阅

题目描述

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。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值