题解:
f[i][0/1][0/1][0/1] 表示从高到低第i位,是否到n的上界,是否到m的上界,当前异或结果是否到k的下界
对于每个状态记录合法方案数和所有合法i,j的异或和,最后把k的那些减去即可
摘自洛谷Fizzmy的题解
标程:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
#define F first
#define S second
int T,p,vis[70][2][2][2],mx;
pii ans,f[70][2][2][2];
ll n,m,k;
void count(ll n){
ll tmp=n;
int cnt=0;
while (tmp) tmp>>=1,cnt++;
mx=max(mx,cnt);
}
void add(ll &x,ll y){
x+=y;
if (x>=p) x-=p;
}
pii dfs(int len,bool n1,bool m1,bool k1){
if (len>mx) return make_pair(1,0);
if (vis[len][n1][m1][k1]) return f[len][n1][m1][k1];
vis[len][n1][m1][k1]=1;
int nn=(n>>mx-len)&1,mm=(m>>mx-len)&1,kk=(k>>mx-len)&1;
for (int i=0;i<=(n1?nn:1);i++)
for (int j=0;j<=(m1?mm:1);j++){
if (k1 && (i^j)<kk) continue;
pii tmp=dfs(len+1,n1&&(i==nn),m1&&(j==mm),k1&&((i^j)==kk));
add(f[len][n1][m1][k1].F,tmp.F);
add(f[len][n1][m1][k1].S,((1ll<<mx-len)*(i^j)%p*tmp.F%p+tmp.S)%p);
}
return f[len][n1][m1][k1];
}
int main(){
scanf("%d",&T);
while (T--){
memset(vis,0,sizeof(vis));
memset(f,0,sizeof(f));
scanf("%lld%lld%lld%d",&n,&m,&k,&p);
mx=0;n--;m--;
count(n);count(m);count(k);
ans=dfs(1,1,1,1);
printf("%lld\n",(ans.S%p-k%p*ans.F%p+p)%p);
}
}