【Luogu】P4067储能表(数位DP)
好的
看到这题之后我一直在想反演,然后想不出来,一度以为自己脑子有问题
然后我脑子真的有问题,这题tm根本就不是反演
设f[i][j][k][l]表示现在已经DP到从高位往低数的第i位,有没有碰到n的上界,有没有碰到m的上界,有没有碰到k的上界
然后记忆化DFS搞一搞,把方案数和异或和都记录下来,最后减掉输出
#include<cstdio> #include<cstring> #include<cctype> #include<cstdlib> #include<algorithm> #define maxn 50 using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } long long n,m,e,mod; struct Ans{ long long xum,sum; }f[100][2][2][2]; bool vis[100][2][2][2]; long long Maxi; void dfs(long long len,long long nown,long long nowm,long long nowk){ if(len>Maxi){ f[len][nown][nowm][nowk]=(Ans){0,1}; return; } if(vis[len][nown][nowm][nowk]) return; Ans &ret=f[len][nown][nowm][nowk]; vis[len][nown][nowm][nowk]=1; long long mn=(n>>(Maxi-len))&1,mm=(m>>(Maxi-len))&1,mk=(e>>(Maxi-len))&1; for(long long i=0;i<=(nown?mn:1);++i) for(long long j=0;j<=(nowm?mm:1);++j){ if(nowk&&(i^j)<mk) continue; dfs(len+1,nown&&(i==mn),nowm&&(j==mm),nowk&&((i^j)==mk)); Ans now=f[len+1][nown&&(i==mn)][nowm&&(j==mm)][nowk&&(i^j)==mk]; ret.sum=(ret.sum+now.sum)%mod; ret.xum=(ret.xum+(1ll<<(Maxi-len))*(i^j)%mod*now.sum+now.xum)%mod; } return; } int main(){ long long T=read(); while(T--){ n=read()-1;m=read()-1;e=read();mod=read(); memset(vis,0,sizeof(vis)); Maxi=0; memset(f,0,sizeof(f)); long long nn=n,nm=m,ne=e;long long cnt=0; while(nn){ cnt++; nn>>=1;} Maxi=max(Maxi,cnt); cnt=0; while(nm){ cnt++; nm>>=1;} Maxi=max(Maxi,cnt); cnt=0; while(ne){ cnt++; ne>>=1;} Maxi=max(Maxi,cnt); dfs(1,1,1,1); Ans now=f[1][1][1][1]; long long ans=(now.xum%mod-(e%mod)*now.sum%mod+mod)%mod; printf("%lld\n",ans); } return 0; }