[SDOI2016]储能表
数位\(dp\)思博题啊
但是我更加思博啊
面对\(10^{18}\)的数据范围,我竟然只开了\(19\)的数据,而这是一道二进制数位\(dp\)啊
我们设\(f[i][0/1][0/1][0/1]\)表示进行到了第\(i\)位,不卡/卡\(n\)的上界,不卡/卡\(m\)的上界,不卡/卡\(k\)的下界,我们求出所有异或值大于\(k\)的数的和,和方案数随便搞一搞就出来了
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline LL read() {
char c=getchar();LL x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int T;
LL n,m,K,mod;
LL dp[67][2][2][2],f[67][2][2][2],pw[67];
int a[67],b[67],c[67];
inline void countBit(LL x,int *g) {
int tot=0;
while(x) {g[tot++]=(x&1ll);x>>=1ll;}
}
int main() {
T=read();
while(T--) {
n=read(),m=read(),K=read(),mod=read();
if(n>m) std::swap(n,m);
n--,m--;
memset(dp,0,sizeof(dp));
memset(f,0,sizeof(f));
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
memset(pw,0,sizeof(pw));
pw[0]=1;
for(re int i=1;i<=65;i++) pw[i]=(pw[i-1]+pw[i-1])%mod;
countBit(n,a),countBit(m,b),countBit(K,c);
f[66][1][1][1]=1;
for(re int i=65;i>=0;--i)
for(re int j=0;j<2;j++)
for(re int k=0;k<2;k++)
for(re int p=0;p<2;p++) {
for(re int t=0;t<2;t++)
for(re int h=0;h<2;h++) {
if(j&&t>a[i]) continue;
if(k&&h>b[i]) continue;
if(p&&(t^h)<c[i]) continue;
int o1=j;
if(o1) if(t<a[i]) o1=0;
int o2=k;
if(o2) if(h<b[i]) o2=0;
int o3=p;
if(o3) if((t^h)>c[i]) o3=0;
f[i][o1][o2][o3]=(f[i][o1][o2][o3]+f[i+1][j][k][p])%mod;
dp[i][o1][o2][o3]=(dp[i][o1][o2][o3]+dp[i+1][j][k][p])%mod;
if(h^t)
dp[i][o1][o2][o3]=(dp[i][o1][o2][o3]+f[i+1][j][k][p]*pw[i]%mod)%mod;
}
}
LL ans=0,cnt=0;
for(re int i=0;i<2;i++)
for(re int j=0;j<2;j++)
for(re int k=0;k<2;k++)
ans=(ans+dp[0][i][j][k])%mod,cnt=(cnt+f[0][i][j][k])%mod;
K%=mod;
printf("%lld\n",(ans-cnt*K%mod+mod)%mod);
}
return 0;
}