储能表
题解
很明显,这道题是暴力。
好吧,很明显暴力只能拿20pts。
用数位dp来完成这道题的做法还是十分普遍的。
首先,我们要用二进制来表示数,毕竟有异或的操作。从第n为往前推,就表示现在是第i位,是否达到上界为n时的最大数,为m时的最大数以及为k时的最大数时的总能量。而表示此时的情况总数。
那么转移式子也很好想了:
,。
而它总共长度为64,于是乎很快就可以得到答案了。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<time.h>
using namespace std;
typedef long long LL;
#define int LL
typedef pair<int,int> pii;
#define gc() getchar()
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=gc();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
x*=f;
}
int t,n,m,k,p,ans;
int dp[100][2][2][2],g[100][2][2][2];
signed main(){
read(t);
while(t--){
read(n);read(m);read(k);read(p);ans=0;n--;m--;
memset(dp,0,sizeof(dp));memset(g,0,sizeof(g));
dp[0][1][1][1]=1;
for(int i=0;i<64;i++)
for(int s1=0;s1<2;s1++)
for(int s2=0;s2<2;s2++)
for(int s3=0;s3<2;s3++){
int j=63-i,num1=n>>j&1,num2=m>>j&1,num3=k>>j&1;
for(int s4=0;s4<2;s4++)
if(!s1||s4<=num1)
for(int s5=0;s5<2;s5++)
if(!s2||s5<=num2){
int s6=s4^s5;
if(!s3||num3<=s6){
int S1=s1&&s4==num1;
int S2=s2&&s5==num2;
int S3=s3&&s6==num3;
dp[i+1][S1][S2][S3]=(dp[i+1][S1][S2][S3]+dp[i][s1][s2][s3])%p;
g[i+1][S1][S2][S3]=(g[i+1][S1][S2][S3]+g[i][s1][s2][s3])%p;
if(s6)g[i+1][S1][S2][S3]=(g[i+1][S1][S2][S3]+(1LL<<j)%p*dp[i][s1][s2][s3]%p)%p;
}
}
}
k%=p;
for(int s1=0;s1<2;s1++)
for(int s2=0;s2<2;s2++)
for(int s3=0;s3<2;s3++)
ans=(ans+g[64][s1][s2][s3]-k*dp[64][s1][s2][s3]%p)%p;
printf("%lld\n",(ans+p)%p);
}
return 0;
}