1. 程式人生 > >Painful Bases LightOJ - 1021

Painful Bases LightOJ - 1021

tin 記憶 type 一個數 code 方案 當前 art csdn

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 }

Painful Bases LightOJ - 1021