hdu 6432 Cyclic
Posted raincle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu 6432 Cyclic相关的知识,希望对你有一定的参考价值。
题意:
计算长度为n且不含有子串[i,i+1]或[n,1]的不同循环排列的个数。
解法:
拿n=4举例,即不含12,23,34,41这样的子串。
首先先说明什么是循环排列:
即把1-n这n个数随意地放到一个圆圈上,循环排列的不同仅仅取决于这n个数的相对位置的不同。
例如1234,2341,3412,4123这些数为相同的循环排列数。
循环排列没有首末之分,这四个元素随便从哪一个元素开始,绕一个方向转过去,都不改变它们的相对顺序;直线排列则首末分明,原来排末位,调换排首位,已改变它们的相对顺序。循环排列与直线排列的主要区别就在这一点上。
从例子看出,直线排列的个数是循环排列个数的n倍
由直线排列个数为n!可推知循环排列个数为(n-1)!。
讲完了循环排列,再来看此题是求不含子串[i,i+1]或[n,1](以下简称顺序子串,共有n个)的循环排列个数
因为一个排列中可能含有多个顺序子串,所以我们列举至少含有0个,1个,...n个的情况 (注意是至少,因为无法保证恰好含有i个)
包含至少一个顺序子串的循环排列数为C(n,1)*(n-2)!
包含至少两个顺序子串的循环排列数为C(n,2)*(n-3)!
...
包含至少k个顺序子串的循环排列数为C(n,k)*(n-k-1)!
(为什么是(n-k-1)! 当你选出了k个子串之后,至少有k+1个数相对位置已被确定,我们让剩下的(n-k-1)个数全排列即可。)
同时注意到包含n个顺序子串的循环排列数一定是1个。
事件之间相互包含,所以用到容斥原理:
∑(k从0到n-1)(-1)^k*C(n,k)*(n-k-1)!+(-1)^n*1
同样预处理阶乘及阶乘的逆元。
AC代码:
1 #include <bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int mod=998244353; 5 const int maxn=1e5+10; 6 ll fac[maxn],invfac[maxn],inv[maxn]; 7 void init() 8 { 9 int i; 10 inv[1]=1; 11 for(i=2;i<maxn;i++) 12 { 13 inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod; 14 } 15 fac[0]=1; 16 invfac[0]=1; 17 for(i=1;i<maxn;i++) 18 { 19 fac[i]=(ll)fac[i-1]*i%mod; 20 invfac[i]=(ll)invfac[i-1]*inv[i]%mod; 21 } 22 } 23 ll C(int n,int m) 24 { 25 if(n<0||m<0||m>n)return 0; 26 return (ll)fac[n]*invfac[m]%mod*invfac[n-m]%mod; 27 } 28 int main() 29 { 30 int t; 31 cin>>t; 32 init(); 33 while(t--) 34 { 35 int n,k; 36 scanf("%d",&n); 37 ll ans=0; 38 for(k=0;k<=n-1;k++) 39 { 40 if(k&1) 41 { 42 ans=(ans-(ll)C(n,k)*fac[n-k-1]%mod+mod)%mod; 43 } 44 else ans=(ans+(ll)C(n,k)*fac[n-k-1]%mod)%mod; 45 } 46 if(n&1)ans-=1; 47 else ans+=1; 48 printf("%I64d ",ans); 49 } 50 return 0; 51 }
以上是关于hdu 6432 Cyclic的主要内容,如果未能解决你的问题,请参考以下文章