Pair(二进制处理+数位dp)(2019牛客暑期多校训练营(第七场))
示例:
输入:
3
3 4 2
4 5 2
7 8 5
输出:5 7 31
题意:存在多少对<x,y>满足x&y>C或x^y<C的条件。(0<x<=A,0<y<=B)
题解:首先逆向考虑,求有多少对(x,y)满足x&y<=C且x^y>=C,然后用A*B去减它即可。然后就是数位dp模板题(数位dp不懂可先补一下知识再看这题),用dp[pos][ia][ib][iand][ixor]表示到第pos位的个数,ia位表示是否是A的上限,ib表示是否是B的上限,iand表示 与 的上限是否是C,ixor表示 异或 的下限是否是C,(当处理至某一位不为上下限时,即其上下限可随便取值,不懂将solve()中//printf("dp[%d][%d][%d][%d][%d]=%lld\n",pos,i,j,i&j,i^j,ans);的斜杠去掉,输入示例看过程)因为该dp表示的值与输入的A,B,C有关,所以每次都要memset置-1。因为dfs存在x或y为0的情况。而x、y最小是1,所以要减掉x或y为0的情况。x为0的个数有max(0,B-C+1),即y>=C;同理,y为0有max(0,A-C+1)种。
code:
#include<bits/stdc++.h> #define LL long long using namespace std; LL A,B,C,dp[40][2][2][2][2]; int t,a[40],b[40],c[40]; LL solve(int pos,int ia,int ib,int iand,int ixor) { if(pos<0)return 1; if(dp[pos][ia][ib][iand][ixor]!=-1)return dp[pos][ia][ib][iand][ixor]; int up1=1,up2=1,up3=1,up4=0; if(ia)up1=a[pos];//如果ia/ib/iand/ixor为0,即此处无上下限约束 if(ib)up2=b[pos]; if(iand)up3=c[pos]; if(ixor)up4=c[pos]; LL ans=0; for(int i=0;i<=up1;i++){ for(int j=0;j<=up2;j++){ if((i&j)>up3)continue; if((i^j)<up4)continue; ans+=solve(pos-1,ia&&(i==a[pos]),ib&&(j==b[pos]),iand&&((i&j)==c[pos]),ixor&&((i^j)==c[pos])); //printf("dp[%d][%d][%d][%d][%d]=%lld\n",pos,i,j,i&j,i^j,ans); } } return dp[pos][ia][ib][iand][ixor]=ans; } int main() { scanf("%d",&t); while(t--){ memset(dp,-1,sizeof(dp)); scanf("%lld%lld%lld",&A,&B,&C); LL tA=A,tB=B,tC=C; memset(a,0,sizeof(a));memset(b,0,sizeof(b));memset(c,0,sizeof(c)); int i=0,j=0,k=0; while(tA||tB||tC){//处理A,B,C的二进制 if(tA){a[i++]=tA&1;tA=tA>>1;} if(tB){b[j++]=tB&1;tB=tB>>1;} if(tC){c[k++]=tC&1;tC=tC>>1;} } LL summ=solve(30,1,1,1,1)-max(A-C+1,0LL)-max(B-C+1,0LL); summ=(A*B)-summ; printf("%lld\n",summ); } return 0; }
越努力越幸运,越辛苦越强大。