2020 ICPC 亞洲區域賽(上海)C Sum of Log (數位dp)
阿新 • • 發佈:2020-12-22
技術標籤:=====DP=====
題鏈:https://ac.nowcoder.com/acm/contest/9925/C
題意:求 。
思路:首先,暴力的話就是列舉i,j。那麼演算法的話就很容易想到數位dp。看到&運算,肯定是二進位制,那就是數二進位制位。
再看,因為要求i&j==0,那麼i和j中每一位中都最多隻能有一個1(每一位只能有00,01,10,3種情況);
又因為log運算,那麼i,j中最高位的1是第幾位就是的值。
那麼,我們列舉每一個最高位(也就是列舉的值),然後數位dp算i&j==0的個數就行了。
(PS:知道是數位dp,奈何不太熟練,不會定狀態,一直T,還是菜。)
程式碼1:
#include <bits/stdc++.h> #define ll long long using namespace std; const ll mod = 1e9+7; const int N = 50; ll dp[N][2][2]; //dp[len][j][k]:從第len位開始數,i是否上限,j是否上限時i&j==0的個數 int digita[N],digitb[N]; ll a,b; ll Mo(ll x){ if(x>=mod) x-=mod; return x; } ll dfs(int len,bool limita,bool limitb){ if(len==0) return 1; if(dp[len][limita][limitb]!=-1) return dp[len][limita][limitb]; int upa=limita?digita[len]:1; int upb=limitb?digitb[len]:1; ll res=0; for(int i=0;i<=upa;i++){ for(int j=0;j<=upb;j++){ int x=i&j; if(x==1) continue; res=(res+dfs(len-1,limita&&i==upa,limitb&&j==upb))%mod; //res=Mo(res); } } return dp[len][limita][limitb]=res; } ll cal(ll a,ll b){ memset(digita,0,sizeof digita); memset(digitb,0,sizeof digitb); int lena=0,lenb=0; while(a) digita[++lena]=(a&1),a>>=1; while(b) digitb[++lenb]=(b&1),b>>=1; ll res=0; memset(dp,-1,sizeof dp); //i的最高位確定Log2(i+j)+1的值 for(int i=lena;i>=1;i--){ //i的第i位為1,j的第i位為0 //i-1:數第i-1位 //i==lena:當i==lena時才是上限,否則不是上限 //i>lenb:當i>lenb時才是上限 res+=(dfs(i-1,i==lena,i>lenb)*i)%mod; res%=mod; } memset(dp,-1,sizeof dp); //j的最高位確定Log2(i+j)+1的值 for(int i=lenb;i>=1;i--){ //i的第i位為0,j的第i位為1 //i-1:數第i-1位 //i>lena:當i>lena時才是上限,否則不是上限 //i==lenb:當i==lenb時才是上限 res+=(dfs(i-1,i>lena,i==lenb)*i)%mod; res%=mod; } return res; } int main(void){ // freopen("input.txt","r",stdin); // freopen("out.txt","w",stdout); int t; scanf("%d",&t); while(t--){ scanf("%lld%lld",&a,&b); printf("%lld\n",cal(a,b)); } return 0; }
程式碼2(超時): T太多了,足足1e5,要是1e4或者1e3應該就過了。
#include <bits/stdc++.h> #define ll long long using namespace std; const int mod = 1e9+7; const int N = 50; int dp[N][N][2][2]; int digita[N],digitb[N]; int a,b; inline int Read() { int res=0,ch,flag=0; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0'; return flag?-res:res; } inline void Write(int a) { if(a>9) Write(a/10); putchar(a%10+'0'); } int Mo(int x){ if(x>=mod) x-=mod; return x; } //len:數到第幾位 //pos: 最高位是第幾位 //limita: i是否為上限 //limitb: j是否為上限 int dfs(int len,int pos,bool limita,bool limitb){ if(len==0) return (pos==33)?1:pos; if(dp[len][pos][limita][limitb]!=-1) return dp[len][pos][limita][limitb]; int upa=limita?digita[len]:1; int upb=limitb?digitb[len]:1; int res=0; for(int i=0;i<=upa;i++){ for(int j=0;j<=upb;j++){ if(i==1&&j==1) continue; int np; if(pos!=33) np=pos; else if(i==0&&j==0) np=pos; else np=len; res=(res+dfs(len-1,np,limita&&i==upa,limitb&&j==upb)),res=Mo(res); } } return dp[len][pos][limita][limitb]=res; } int cal(int a,int b){ int lena=0,lenb=0; while(a) digita[++lena]=(a&1),a>>=1; while(b) digitb[++lenb]=(b&1),b>>=1; int len=max(lena,lenb); int i=lena+1; while(i<=len) digita[i++]=0; i=lenb+1; while(i<=len) digitb[i++]=0; for(int i=len;i>=1;i--){ dp[i][33][0][0]=dp[i][33][0][1]=dp[i][33][1][0]=dp[i][33][1][1]=-1; for(int j=len;j>=1;j--){ for(int k=0;k<=1;k++){ for(int x=0;x<=1;x++){ dp[i][j][k][x]=-1; } } } } return dfs(len,33,1,1); } int main(void){ // freopen("input.txt","r",stdin); // freopen("out.txt","w",stdout); int t; t=Read(); while(t--){ a=Read(),b=Read();; printf("%d\n",Mo(cal(a,b)-1+mod)); } return 0; }