【HDU 6021】 MG loves string (枚举+容斥原理)

MG loves string

 Accepts: 30
 Submissions: 67
 Time Limit: 2000/1000 MS (Java/Others)
 Memory Limit: 262144/262144 K (Java/Others)
问题描述
MG是一个很忙碌的男孩子。今天他沉迷于这样一个问题:

对于一个长度为N的由小写英文字母构成的随机字符串,当它进行一次变换,所有字符i都会变成a[i]MG规定所有a[i]构成了26个字母组成的排列。

MG现在需要知道这个随机串变换到自身的期望变换次数。请你输出期望答案乘上26^n以后模 1000000007的结果。

MG认为这件事非常容易,不屑于用计算机解决,于是运用他高超的人类智慧开始进行计算。作为一名旁观者,你也想挑战MG智慧,请你写个程序,计算答案。
输入描述
第一行一个整数T,代表数据组数(1 <=T<=10)。

接下来,对于每组数据——

第一行一个整数N,表示给定的随机串长度(1<=N<=1000000000)。

第二行26个字母,表示a_i​​序列
输出描述
对于每一组数据,输出一行。

显然,这个期望是一个实数。请你输出它乘上26^N​​以后模 1000000007 的结果
输入样例
2
2
abcdefghijklmnpqrstuvwxyzo
1
abcdefghijklmnopqrstuvwxyz
输出样例
5956
26


 

【分析】

  感觉BC的题挺好的啊【每次都能学到东西。。

  首先,知道,这是个带LCM的期望。就是看随机串分别在长度为几的循环节里面,然后LCM。

  然后,不同长度的循环节不会超过6个,1+2+3+4+5+6=21。

  就是根据输入的那个串,只会有6种长度的循环节,所以你可以枚举真正的随机串涵盖的循环节有哪几个,枚举是2^6。

  然后就是把n个字符放到那些循环节的字母集合中去,但是要保证每个循环节都一定有一个字母覆盖,问它的方案数。

  其实这是经典的容斥原理,就是n个东西分到m个集合,让每个集合都至少有一个东西。

  这里我们枚举子集就可以用容斥原理计算出来了【注意容斥,你要减掉的是没有涵盖某一个集合的,加上没有涵盖两个集合的。。。】

  枚举子集是3^n(用二项式定理易证)

  这个可以预处理的。

  所以是$O(2^6*\log(n)+3^6)$

 

官方题解:

 

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 using namespace std;
 7 #define Mod 1000000007
 8 #define LL long long
 9 
10 int u[30],v[10],pp[10],n;
11 LL sm[1010];
12 int h[1010],ss[1010],p[30];
13 char s[30];
14 bool vis[30];
15 
16 LL qpow(LL x,int b)
17 {
18     x%=Mod;
19     LL ans=1;
20     while(b)
21     {
22         if(b&1) ans=(ans*x)%Mod;
23         x=(x*x)%Mod;
24         b>>=1;
25     }
26     return ans;
27 }
28 
29 void init()
30 {
31     memset(vis,0,sizeof(vis));
32     memset(p,0,sizeof(p));
33     memset(h,0,sizeof(h));
34     memset(ss,0,sizeof(ss));
35     scanf("%d",&n);
36     scanf("%s",s+1);
37     for(int i=1;i<=26;i++) u[i]=s[i]-'a'+1;
38     for(int i=1;i<=26;i++) if(!vis[i])
39     {
40         vis[i]=1;
41         int x=i,cnt=1;
42         while(u[x]!=i) x=u[x],cnt++,vis[x]=1;
43         p[cnt]++;
44     }
45     v[0]=0;
46     for(int i=1;i<=26;i++) if(p[i]) v[++v[0]]=i,pp[v[0]]=p[i];
47     for(int i=0;i<(1<<v[0]);i++)
48      for(int j=1;j<=6;j++) if(i&(1<<j-1)) ss[i]+=v[j]*pp[j],h[i]++;
49     for(int i=0;i<(1<<v[0]);i++) sm[i]=qpow(ss[i],n);sm[0]=0;
50     int i;
51     // for(i=0;i<(1<<v[0]);i++)
52     for(i=(1<<v[0])-1;i>=0;i--)
53      for(int j=i;j;j=(j-1)&i)
54      {
55          if(i==j) continue;
56          if((h[i]-h[j])%2==0) sm[i]+=sm[j];
57          else sm[i]-=sm[j];
58          sm[i]=(sm[i]%Mod+Mod)%Mod;
59      }
60 }
61 
62 LL gcd(LL a,LL b)
63 {
64     if(b==0) return a;
65     return gcd(b,a%b);
66 }
67 
68 LL ans;
69 
70 void ffind(int x,int y,LL nw)
71 {
72     if(x==v[0]+1)
73     {
74         ans=(ans+sm[y]*nw)%Mod;
75         return;
76     }
77     ffind(x+1,y,nw);
78     ffind(x+1,y|(1<<x-1),nw*(LL)v[x]/gcd(nw,v[x]));
79 }
80 
81 int main()
82 {
83     int T;
84     scanf("%d",&T);
85     while(T--)
86     {
87         init();
88         ans=0;ffind(1,0,1);
89         printf("%lld\n",ans);
90     }
91     return 0;
92 }
View Code

 

 

2017-04-02 10:41:18

 

posted @ 2017-04-02 10:39  konjak魔芋  阅读(820)  评论(1编辑  收藏  举报