Painful Bases LightOJ - 1021
题意:给出0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F中的一些字符(不重复)还有一个进制base,求这些字符的排列形成的base进制数中,有多少个能被k整除。
方法:
正常的做法:
http://blog.csdn.net/chenzhenyu123456/article/details/51155038
自己的做法(常数很大,仅做记录):
ans[i][S][j]表示i进制下,S集合中数字可用,总产生余数为j时方案数
digit(S)表示S集合中元素数量。那么,当前数字为一个digit(S)位的base进制数,其第digit(S)位的值转换为10进制后除以k产生的余数就是j。
ans[i][S][j]=sum{ans[i][S-p][(j+k-pow(i,digit(S)-1)*p%k)%k}
其含义:把S中任意一个数字p当做首位,首位产生的余数$pow(i,digit(S)-1)*p%k$,然后剩下数字的余数应该是$(j+k-pow(i,digit(S)-1)*p%k)%k$。
实际使用时,这样定义状态会爆空间,因此只能把i的一维舍去,每组数据都重新算。
错误次数:2
原因:
1.用循环,被卡常,改成了记忆化搜索。
2.在用循环的时候,为了卡常,改了循环内外层,但不正确。
卡常记录:把记录a^b改成记录所有a^b%k,时间缩短到1/3。
1 #include<cstdio> 2 #include<cstring> 3 typedef long long LL; 4 LL ans[70000][22]; 5 LL T,TT; 6 bool ok[20]; 7 char ss[20]; 8 LL popcount[70000]; 9 LL powww[30][30][30]; 10 LL base,k; 11 LL poww(LL a,LL b) 12 { 13 if(powww[a][b][k]) return powww[a][b][k]; 14 LL base=a,ans=1,b1=b; 15 while(b) 16 { 17 if(b&1) ans=ans*base%k; 18 b>>=1; 19 base=base*base%k; 20 } 21 return powww[a][b1][k]=ans; 22 } 23 LL get(LL s,LL j) 24 { 25 if(ans[s][j]!=-1) return ans[s][j]; 26 int j1; 27 ans[s][j]=0; 28 for(j1=0;j1<base;j1++) 29 if(s&(1<<j1)) 30 ans[s][j]+=get(s^(1<<j1),(j+k-poww(base,__builtin_popcount(s)-1)*j1%k)%k); 31 return ans[s][j]; 32 } 33 int main() 34 { 35 LL i,tt; 36 for(i=1;i<(1<<16);i++) 37 popcount[i]=__builtin_popcountll(i); 38 scanf("%lld",&T); 39 for(TT=1;TT<=T;TT++) 40 { 41 tt=0; 42 memset(ok,0,sizeof(ok)); 43 memset(ans,-1,sizeof(ans)); 44 scanf("%lld%lld",&base,&k); 45 ans[0][0]=1; 46 scanf("%s",ss); 47 for(i=0;i<strlen(ss);i++) 48 if(ss[i]>='0'&&ss[i]<='9') 49 ok[ss[i]-'0']=1; 50 else 51 ok[ss[i]-'A'+10]=1; 52 for(i=0;i<base;i++) 53 if(ok[i]) 54 tt^=(1<<i); 55 printf("Case %lld: %lld\n",TT,get(tt,0)); 56 } 57 return 0; 58 }