题目大意:用K种珍珠组成一条项链,要求项链内每种珍珠都要出现,求长度为1~N的符合条件的项链种数(项链看作串即可,不用考虑环状)
比如2种珍珠,N=3,有8种情况:12 21 112 121 211 221 212 122
思路:K种珍珠组成长度为N的项链(不一定K种全出现)有K^N种,减去只有K'(1<=K'<K)种珍珠组成的项链的种数
来看K=5,项链长度为某固定值时:
一、用5种珍珠组成此项链,用到的珍珠种类如下:12345 种数:5^N
二、如果只用其中4种组成,“打算用到的珍珠种类”的搭配为:1234 1235 1345 1245 2345 (比如项链:15235135235,“打算用到的珍珠种类”的搭配就是 1235。不过,项链:15351535135或11111111111也属于此种情况,“打算用”=。=)这些情况下项链的种数是要被减去的。每种搭配有4^N种,共:5*4^N
三、如果只用其中3种组成,“打算用到的珍珠种类”的搭配为:
123 124 125 134 135 145
234 235 245
345
可以知道这些情况在“二”中都被减了两次(例:123在1234 1235的情况中都被减去了),所以得各加1次 种数:10*3^N
四、如果只用其中2种组成,“打算用到的珍珠种类”的搭配为:
12 13 14 15 23 24 25 34 35 45
可以知道这些情况在“二”中都被减了3次,在“三”中都被加了3次,所以还得各减1次 种数:10*2^N
五、如果只用其中1种组成,“打算用到的珍珠种类”的搭配为:
1 2 3 4 5
可以知道这些情况在“二”中都被减了4次,在“三”中都被加了6次,在“四”中都被减了4次,所以还得各加1次 种数:5*1^N
有两个很明显的规律,1、加减情况是交替的;2、每种搭配情况都是减1次或者加1次
当K变其他值时,这是否符合呢?
证明:可得K-1种珍珠时,有C(K,1)种搭配,每种搭配都减1次,满足规律
当(K-1)至(Kx+1)(Kx<K)都满足规律时,可得Kx种珍珠的每种搭配已经进行了如下操作:
-C(K-Kx,K-Kx-1)+C(K-Kx,K-Kx-2)-……C(K-Kx,1) ①
举例说明下:K=5时,2种珍珠的某种搭配:12,它在4种珍珠的情况下减了3次(见上面的“二”,3次分别是1234 1235 1245)。
在3种珍珠的情况下加了3次(见上面的“三”,3次分别是123 124 125)。根据排列组合就知道这个次数该怎么求了,上面的式子就是这样得到。
当K-Kx为奇数时,容易证明式①等于0,证明略,所以此时还需再减1(例如当Kx等于K-1时,K-Kx=1)
当K-Kx为偶数时,式①等于-2,所以此时需要再加1
证明:设K-Kx=X
-C(X,X-1)+C(X,X-2)-……-C(X,1)
=2*(-C(X,1)+C(X,2)-C(X,3)……)+(-1)^(X/2)*C(X,X/2)
=2*(-C(X-1,0)-C(X-1,1)+C(X-1,1)+C(X-1,2)-C(X-1,2)-C(X-1,3)……)+(-1)^(X/2)*C(X,X/2)
=2*(-1)+(-1)^(X/2)*[ -2*C(X-1,X/2-1)+C(X,X/2) ]
=-2
所以之前发现的规律始终成立。
因此N的长度时,种数为C(K,0)*K^N-C(K,1)*(K-1)^N+C(K,2)*(K-2)^N……
把长度为1~N时的种数全加起来就是答案了。
用DP可以求出K^[2^(0~31)]的值=。=
也可以求出K+K^2+……+K^[2^(0~31)]的和O_O
先存起来,然后类似快速幂什么的搞搞就好了
#include<iostream> #define M 1234567891 int N,K; __int64 Kpow[31][40],Ksum[31][40],C[31][31]; __int64 mod(__int64 x){ return (x%M+M)%M; } void get_data(){ int i,j; for(j=1;j<31;j++){ Kpow[j][0]=j; for(i=1;i<32;i++)Kpow[j][i]=(Kpow[j][i-1]*Kpow[j][i-1])%M; Ksum[j][0]=j; for(i=1;i<32;i++)Ksum[j][i]=(Kpow[j][i-1]+1)*Ksum[j][i-1]%M; } for(i=1;i<31;i++){ C[i][0]=C[i][i]=1; for(j=1;j<i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%M; } } __int64 getv(int k,int n){ __int64 res=0,last=1; int cnt=0; while(n){ if(n&1){ res+=Ksum[k][cnt]*last; res%=M; last*=Kpow[k][cnt]; last%=M; } cnt++; n>>=1; } return res; } void run(){ int i; __int64 res=0; int sym=1; for(i=0;i<K;i++){ res+=sym*C[K][i]*getv(K-i,N); sym=-sym; res=mod(res); } /* K^N-K(K-1)^N+C(2,K)*(K-2)^N......... */ printf("%I64d/n",res); } int main(){ // printf("start/n"); get_data(); int t; scanf("%d",&t); while(t--){ scanf("%d%d",&N,&K); run(); } return 0; }
自己写了才知道:有些看起来就头大的解题报告其实不一定很复杂=。=