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\n",ans);
49     }
50     return 0;
51 }

 

posted @ 2018-08-23 00:13  raincle  阅读(343)  评论(0编辑  收藏  举报