1. 程式人生 > >【BZOJ4513】儲能表(SDOI2016)-數位DP

【BZOJ4513】儲能表(SDOI2016)-數位DP

測試地址:儲能表
做法: 本題需要用到數位DP。
顯然地,我們可以把問題轉化成,計算滿足這些條件: i n 1 , j

m 1 , ( i   x o r  
j ) k i\le n-1,j\le m-1,(i\space xor\space j)\ge k 時,滿足要求的數對 ( i
, j ) (i,j)
的數目以及此時 i   x o r   j i\space xor\space j 的和。
我們發現這三種限制都可以按二進位制位決策,而且這三個限制都是一些上界或者下界,啟發我們使用數位DP,因此我們用傳統的狀態定義定義一個四維的狀態:令 c n t ( d , 0 / 1 , 0 / 1 , 0 / 1 ) cnt(d,0/1,0/1,0/1) 為前 d d 位中,滿足 i i 不卡/卡上界, j j 不卡/卡上界, i   x o r   j i\space xor\space j 不卡/卡下界的數對 ( i , j ) (i,j) 數目,再類似定義 s u m ( d , 0 / 1 , 0 / 1 , 0 / 1 ) sum(d,0/1,0/1,0/1) 為滿足這麼多條件的所有 i   x o r   j i\space xor\space j 之和,就可以像正常數位DP一樣轉移了,時間複雜度為 O ( T 64 2 5 ) O(T\cdot 64\cdot 2^5)
以下是本人程式碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,d,n[70],m[70],k[70];
ll N,M,K,theK,p,cnt[70][2][2][2],sum[70][2][2][2];

void add(ll &v,ll val)
{
	v=((v+val)%p+p)%p;
}

void solve()
{
	memset(sum,0,sizeof(sum));
	memset(cnt,0,sizeof(cnt));
	cnt[d+1][1][1][1]=1;
	for(int i=d;i>=1;i--)
	{
		for(int s1=0;s1<=1;s1++)
			for(int s2=0;s2<=1;s2++)
				for(int s3=0;s3<=1;s3++)
					for(int ki=0;ki<=1;ki++)
						for(int kj=0;kj<=1;kj++)
						{
							bool nxts1=0,nxts2=0,nxts3=0;
							if (s1)
							{
								if (ki>n[i]) continue;
								else if (ki==n[i]) nxts1=1;
							}
							if (s2)
							{
								if (kj>m[i]) continue;
								else if (kj==m[i]) nxts2=1;
							}
							if (s3)
							{
								if ((ki^kj)<k[i]) continue;
								else if ((ki^kj)==k[i]) nxts3=1;
							}
							add(cnt[i][nxts1][nxts2][nxts3],cnt[i+1][s1][s2][s3]);
							add(sum[i][nxts1][nxts2][nxts3],(sum[i+1][s1][s2][s3]<<1)+cnt[i+1][s1][s2][s3]*(ki^kj));
						}
	}
	ll ans=0;
	theK%=p;
	for(int s1=0;s1<=1;s1++)
		for(int s2=0;s2<=1;s2++)
			for(int s3=0;s3<=1;s3++)
				add(ans,sum[1][s1][s2][s3]-cnt[1][s1][s2][s3]*theK);
	printf("%lld\n",ans);
}

int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%lld%lld%lld%lld",&N,&M,&K,&p);
		N--,M--;
		
		d=0;
		theK=K;
		while(N||M||K)
		{
			++d;
			n[d]=N%2ll;
			m[d]=M%2ll;
			k[d]=K%2ll;
			N>>=1,M>>=1,K>>=1;
		}
		
		solve();
	}
	
	return 0;
}