题目链接:http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=34985
题目大意:问n长度的串用0~k的数字去填,有多少个串保证任意子串中不包含0~k的某一个全排列
邀请赛上A的较多的一道题,比赛的时候死活想不出,回来之后突然就想通了,简直..... = =!
解题思路:
对于所有串我们都只考虑末尾最多有多少位能构成全排列的一部分(用l来表示),即最多有多少位不重复的数字出现,将问题转化为求末尾最多有k位能构成全排列的串的总数量
假设k为5,有一个串……0023,不难发现l=3
我们以这个串出发在之后添上数字,假如我们添的是0、2、3中的一个:
0: ……00230 (l=3)
2: ……00232 (l=2)
3: ……00233 (l=1)
假如是l长度中没有出现过的数字
则构成新串 ……00231 ……00234 ……00235 l=4
最后可以得到规律:总长度为n串中 l=m的串的数量 x1 得到 总长度为n+1的串中 l=(1,2,……,m)的串
总长度为n串中 l=m的串的数量 x(k-m+2) 得到 总长度为n+1的串中 l=m+1的串
用mar[i][j]来表示由l=j的串得到l=i的串所以
mar可以表示为(以k=5为例)
1 1 1 1 1
5 1 1 1 1
0 4 1 1 1
0 0 3 1 1
0 0 0 2 1
通过该矩阵我们可以由长度为n的串数量可以推出长度为n+1的串的数量:
于是我们可以通过长度1的串最终得到总长度为n的串, n=1时只有l最多为1 总数为 k+1
快速幂求得该矩阵的(n-1)次幂,该矩阵的第一列相加乘(k+1)即为最终结果
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 #define FFF 20140518 8 struct node{ 9 long long mar[15][15]; 10 }sor; 11 void init(int k) 12 { 13 memset(sor.mar,0,sizeof(sor.mar)); 14 for(int i=1;i<=k;i++) 15 { 16 for(int j=i;j<=k;j++) 17 { 18 sor.mar[i][j]=1; 19 } 20 if(i>1) 21 { 22 sor.mar[i][i-1]=k-i+2; 23 } 24 } 25 } 26 node marMulti(node a,node b,int k) 27 { 28 node ret; 29 memset(ret.mar,0,sizeof(ret.mar)); 30 for(int i=1;i<=k;i++) 31 { 32 for(int j=1;j<=k;j++) 33 { 34 for(int l=1;l<=k;l++) 35 { 36 ret.mar[i][j]+=(a.mar[i][l]*b.mar[l][j])%FFF; 37 ret.mar[i][j]%=FFF; 38 } 39 } 40 } 41 return ret; 42 } 43 node matrixPow(long long x,int k) 44 { 45 node now=sor; 46 node ret; 47 memset(ret.mar,0,sizeof(ret.mar)); 48 for(int i=1;i<=k;i++) 49 ret.mar[i][i]=1; 50 while(x) 51 { 52 if(x%2==1) 53 ret=marMulti(now,ret,k); 54 x/=2; 55 now=marMulti(now,now,k); 56 } 57 return ret; 58 } 59 void print(node sor,int k) 60 { 61 for(int i=1;i<=k;i++) 62 { 63 for(int j=1;j<=k;j++) 64 { 65 cout<<sor.mar[i][j]<<' '; 66 } 67 cout<<endl; 68 } 69 } 70 int main() 71 { 72 int keng,k,Case=1; 73 long long n; 74 scanf("%d",&keng); 75 while(keng--) 76 { 77 scanf("%lld%d",&n,&k); 78 init(k); 79 node ret=matrixPow(n-1,k); 80 int ans=0; 81 // print(sor,k); 82 // print(ret,k); 83 for(int i=1;i<=k;i++) 84 { 85 ans+=(ret.mar[i][1]*(k+1))%FFF; 86 ans%=FFF; 87 } 88 printf("Case #%d: %d ",Case++,ans); 89 } 90 return 0; 91 }