码迷,mamicode.com
首页 > 其他好文 > 详细

AGC008E Next or Nextnext

时间:2020-04-06 00:13:59      阅读:69      评论:0      收藏:0      [点我收藏+]

标签:operator   link   inline   http   乘法   利用   include   read   next   

Link
如果我们\(i\rightarrow p_i\)建边,那么最后会得到一些有向环。
对于上述的每一个环,如果我们\(i\rightarrow a_i\)建边,那么此时\(a_i\)\(i\)在顺时针方向下的第\(1\operatorname{or}2\)个点,也就是\(a_i=p_i\vee a_i=p_{p_i}\)
那么此时有四种情况:
\(a_i=p_i\),那么这个环不变。
\(a_i=p_{p_i}\),那我们对环长的奇偶性分开讨论。
若环长为奇数,那么这个环变成元素相同的另一个环。
若环长为偶数,那么这个环会被拆成两个环。
否则部分\(a_i=p_i\),部分\(a_i=p_{p_i}\),那么这个环会被拆成一个环和若干个指向环的链构成的基环内向树。
现在我们只有\(i\rightarrow a_i\)建出来的图,所以我们倒过来考虑。
如果这不是由若干个环和基环内向树构成的森林,那么答案为\(0\)
求出对于任意一个\(l\),有多少个环长度为\(l\),然后利用do求出对于一个环长而言的方案数,最后再根据乘法原理乘起来就好了。
假如现在长度为\(l\),设\(f_i\)表示有\(i\)个长度为\(l\)的环的方案数,那么有:

\[f_j= \begin{cases} 1&j=0\1+[i\ne1\wedge2\nmid i]&j=1\i(j-1)f_{j-2}+f_if_{j-1}&\text{otherwise} \end{cases} \]

然后就只需要考虑基环内向树的方案数了。
我们把每条链(包括环上的点)单独拿出来,那么这个环会被剖成很多份。
假设一条链长为\(l\),从这条链顺时针的一段环的长度为\(d\),那么:
\(d\le l\),则答案为\(0\);若\(d=l\),则方案数为\(1\);若\(d>l\),则方案数为\(2\)
然后根据乘法原理把基环内向树的答案和环的答案乘在一起就好了。

#include<cstdio>
#include<cctype>
const int N=100007,P=1000000007;
int a[N],deg[N],vis[N],pre[N],cnt[N],f[N];
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
int add(int a,int b){return a+=b-P,a+(a>>31&P);}
int mul(int a,int b){return 1ll*a*b%P;}
int main()
{
    int n=read(),ans=1;
    for(int i=1;i<=n;++i) ++deg[a[i]=read()];
    for(int i=1,p;(p=i)<=n;++i)
    {
	if(deg[i]>2) return puts("0"),0;
	if(deg[i]<2||vis[i]) continue;
	do
	{
	    if(vis[p]) return puts("0"),0;
	    vis[p]=1,pre[a[p]]=p,p=a[p];
	}while(p^i);
    }
    for(int i=1,p,l1,l2;i<=n;++i)
	if(!deg[i])
	{
	    for(p=i,l1=0,l2=0;!vis[p];p=a[p]) vis[p]=1,++l1;
	    do ++l2,p=pre[p]; while(deg[p]^2);
	    if(l1<l2) ans=add(ans,ans); else if(l1>l2) return puts("0"),0;
	}
    for(int i=1;i<=n;++i)
	if(!vis[i])
	{
	    int p=i,l=0;
	    do ++l,p=a[p],vis[p]=1; while(p^i);
	    ++cnt[l];
	}
    for(int i=1;i<=n;ans=mul(ans,f[cnt[i++]]))
    {
	f[0]=1,f[1]=1+(i^1&&i&1);
	for(int j=2;j<=cnt[i];++j) f[j]=add(mul(i,mul(j-1,f[j-2])),mul(f[1],f[j-1]));
    }
    printf("%d",ans);
}

AGC008E Next or Nextnext

标签:operator   link   inline   http   乘法   利用   include   read   next   

原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12639824.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!