题意,给定数字L,M,K,N 。
L 个数字,每个数字的取值范围是 1 , 2 , ... , M 。
在符合上述条件的所有组合中,L个数字之和为K的倍数的组合 称为一个状态。
将所有可能的状态按字典序排序,编号从0开始。
求编号为 N 的那个状态。
L=100,M=50 ; K=1 的时候,每种组合都是状态,总共 50的100次方种组合。
所以 N 的上限是 50 的100次方 -1。
于是要写一个大数类。只需要 加,减,比较 操作。
然后 F[ i ] [ j ] 表示,i 个数,数字的和除以K的余数为 j 的情况种数。
然后从最高位开始一个一个确定数字就行了。
#define FOR(i,n) for(long long (i)=1;(i)<=(n);(i)++)
#define For(i,n) for(long long (i)=0;(i)<(n);(i)++)
using namespace std;
struct Int{
char a[1000];
int len;
Int(int k){*this=k;}
Int(){*this=0;}
void operator =(int k){
if(!k){len=1;a[0]=0;return;}
len=0;
while(k) a[len++]=k%10,k/=10;
}
void operator =(string k){
len=k.length();
for(int i=0;i<len;i++) {
a[i]=k[len-i-1]-48;
}
}
Int operator +(Int &B){
Int C;C.len=len>B.len?len:B.len;
for(int i=len;i<C.len;i++) a[i]=0;
for(int i=B.len;i<C.len;i++)B.a[i]=0;
int d=0;
for(int i=0;i<C.len;i++){
C.a[i]=a[i]+B.a[i]+d;
d=C.a[i]/10;
C.a[i]%=10;
}
if(d) C.a[C.len++]=d;
return C;
}
Int operator +=(Int B){
*this=*this+B;
return *this;
}
Int operator -(Int &B){
Int C;C.len=len;
for(int i=B.len;i<C.len;i++)B.a[i]=0;
int d=0;
for(int i=0;i<C.len;i++){
C.a[i]=a[i]-B.a[i]-d;
d=0;
while(C.a[i]<0) d++,C.a[i]+=10;
}
while(C.len>0&&!C.a[C.len-1]) C.len--;
if(C.len==0) C=0;
return C;
}
bool operator>=(Int B){
if(len>B.len) return 1;
if(len<B.len) return 0;
for(int i=len-1;i>=0;i--){
if(a[i]>B.a[i]) return 1;
if(a[i]<B.a[i]) return 0;
}
return 1;
}
void show(){
for(int i=len-1;i>=0;i--) printf("%d",a[i]);
}
};
Int f[101][50];
int s[101];
int L,M,K;
string N;Int NN;
void F(Int k){
int CurR=0;
FOR(i,L){
s[i]=1;
while(k>=f[L-i][(K-(CurR+s[i])%K)%K]){
k=k-f[L-i][(K-(CurR+s[i])%K)%K];
s[i]++;
if(s[i]==M) break;
}
CurR+=s[i];
CurR%=K;
}
FOR(i,L){
printf("%d",s[i]);
if(i==L) printf("\n");
else printf(" ");
}
}
int main(void)
{
while(cin>>L>>M>>K>>N){
For(i,101) For(j,50) f[i][j]=0;
f[0][0]=1;
For(i,L){
For(j,K){
FOR(k,M){
f[i+1][(j+k)%K]+=f[i][j];
}
}
}
NN=N;//string 转换到 Int
F(NN);//根据编号确定组合。
}
return 0;
}