儲能表
題解
很明顯,這道題是暴力。
好吧,很明顯暴力只能拿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;
}