[SDOI2016]储能表

储能表

题解

很明显,这道题是暴力。

好吧,很明显暴力只能拿20pts。

用数位dp来完成这道题的做法还是十分普遍的。

首先,我们要用二进制来表示数,毕竟有异或的操作。从第n为往前推,g_{i,s1,s2,s3}就表示现在是第i位,是否达到上界为n时的最大数,为m时的最大数以及为k时的最大数时的总能量。而dp_{i,s1,s2,s3}表示此时的情况总数。

那么转移式子也很好想了:

dp_{i,S1,S2,S3}+=dp_{i-1,s1,s2,s3}g_{i,S1,S2,S3}+=g_{i-1,s1,s2,s3}+[S]2^{i-1}dp_{i-1,s1,s2,s3}

而它总共长度为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;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值