数位DP博大精深阿弥陀佛。
(回归正题)woc这取模要死,乘法到处爆炸,一不小心就炸longlong,考试肯定fst惨挂只有暴力分。
对于子问题F(n,m,k)求∑[0<=i<n]∑[0<=j<m] [i xor j<=k] i xor j
首先将n和m自减后同k一起二进制分解,统一长度后按从高位到低位排,然后我们就可以DP了。
这里DP[t][q][k][y][p]表示:
t表示当前进行到的位数,即这个子问题求解到了分解出的二进制的前t位。
当p=0,这个值表示子问题中,满足i xor j<=k[1...t]的总的二元组(i,j)的个数
当p=1表示这些所有的二元组(i,j)的i xor j之和
当q=1,表示被限定为i=n[1...t],否则i<n[1...t];当k=1,表示被限定为j=m[1...t],否则j<m[1...t]
当y=1,限定i xor j=k[1...t],否则i xor j<k[1...t]
这样我们考虑从DP[i-1][j][k][t][p]向上转移
显然我们分别考虑这一次对于i和j在末尾的选择x和y,就可以得出新的newj,newk和newt,讨论一下即可。
然后我们的值转移显然是
DP[i][newj][newk][newt][0]+=DP[i-1][j][k][t][0]
DP[i][newj][newk][newt][1]+=DP[i-1][j][k][t][1]+(x xor y)DP[i-1][j][k][t][0]
根据容斥原理,我们只需要算出A(x1,y1)=F(n,m,max(n,m)*2(使位数更多))和B(x2,y2)=F(n,m,k),(x和y分别表示方案数和和)那么
ans=y1-k*x1+k*x2-y2
#include"bits/stdc++.h"
using namespace std;
typedef long long ll;
ll n,m,k;int P;
const int N=65;
ll dp[N][2][2][2][2];
int fa[N],fb[N],fc[N],l;
int C;
inline void ADD(ll&x,ll y){(x+=y)%=P;}
int makedp(ll n,ll m,ll k,int P){
memset(fa,0,sizeof(fa));memset(fb,0,sizeof(fb));
memset(fc,0,sizeof(fc));memset(dp,0,sizeof(dp));
int i,j,t,a,b,p;n--,m--; dp[0][1][1][1][0]=1;
for(i=1,j=0;n;n>>=1,i++)fa[i]=n%2;l=max(l,i-1);
for(i=1,j=0;m;m>>=1,i++)fb[i]=m%2;l=max(l,i-1);
for(i=1,j=0;k;k>>=1,i++)fc[i]=k%2;l=max(l,i-1);
for(i=1;2*i<=l;i++){
swap(fa[i],fa[l-i+1]);
swap(fb[i],fb[l-i+1]);
swap(fc[i],fc[l-i+1]);
}
for(i=1;i<=l;i++){
for(j=0;j<=1;j++){
for(a=0;a<=1;a++){
for(b=0;b<=1;b++){
if(dp[i-1][j][a][b][0]==0) continue;
for(t=0;t<=1;t++)for(p=0;p<=1;p++){
if((t^p)>fc[i]&&b||t>fa[i]&&j||p>fb[i]&&a) continue;
ADD(dp[i][j&&t==fa[i]][a&&p==fb[i]][b&&(t^p)==fc[i]][0],dp[i-1][j][a][b][0]);
ADD(dp[i][j&&t==fa[i]][a&&p==fb[i]][b&&(t^p)==fc[i]][1],dp[i-1][j][a][b][1]*2+(t^p)*dp[i-1][j][a][b][0]);
}
}
}
}
}
return l;
}
void work(){
scanf("%lld%lld%lld%d",&n,&m,&k,&P);
ll ans=0,sum=0;int i,j,l,t;l=makedp(n,m,max(n,m)<<1,P);
for(i=0;i<=1;i++)for(j=0;j<=1;j++)
ans=(ans+dp[l][i][j][0][1])%P;
l=makedp(n,m,k,P);ans=((ans-n%P*(m%P)%P*(k%P))%P+P)%P;
for(i=0;i<=1;i++)for(j=0;j<=1;j++)for(t=0;t<=1;t++)
ans=((ans+k%P*(dp[l][i][j][t][0]%P)%P-dp[l][i][j][t][1])%P+P)%P;
printf("%lld\n",ans);
}
int main(){
int T;cin>>T;
while(T--)work();
return 0;
}