[SDOI2016]储能表

数位dp题。

f[i][0/1][0/1][0/1]表示从高到低第i位,是否到n的上界,是否到m的上界,当前异或结果是否到k的下界。

对于每个状态记录合法方案数和所有合法i,j的异或和,对于每一位枚举n和m选0还是1转移即可。

#include<bits/stdc++.h>
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout);
#define N 2000000
#define inf 123456789123456789
#define db double
//#define P 1000000007
#define int long long
#define mid (l+r>>1)
#define d(x) (x>>cur-1&1)
using namespace std;
int n,m,k,P,T;
struct node{int x,y;}mp[70][2][2][2];
node dp(int cur,int nx,int mx,int kx){
	if(!cur) return {1,0};
	if(mp[cur][nx][mx][kx].x) return mp[cur][nx][mx][kx];
	node res={0,0};
	for(int i=0;i<=(d(n)||!nx);i++){
		for(int j=0;j<=(d(m)||!mx);j++){
			if(kx&&d(k)>(i^j)) continue;
			node v=dp(cur-1,nx&&i==d(n),mx&&j==d(m),kx&&(i^j)==d(k));
			(res.x+=v.x)%=P;
			(res.y+=(((i^j)<<(cur-1))%P*v.x%P+v.y)%P)%=P;
		}
	}
	return mp[cur][nx][mx][kx]=res;
}
signed main(){
	scanf("%lld",&T);
	while(T--){
		memset(mp,0,sizeof(mp));
		scanf("%lld%lld%lld%lld",&n,&m,&k,&P);n--;m--;
		node v=dp(63,1,1,1);
		printf("%lld
",(v.y-k%P*v.x%P+P)%P);
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/blogoflyn/p/13269823.html