您的位置:首页 > 其它

HDU 2294 Pendant

2011-06-03 23:40 239 查看
题目大意:用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;
}


 

自己写了才知道:有些看起来就头大的解题报告其实不一定很复杂=。=

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c