【BZOJ4513】儲能表(SDOI2016)-數位DP
阿新 • • 發佈:2018-11-07
測試地址:儲能表
做法: 本題需要用到數位DP。
顯然地,我們可以把問題轉化成,計算滿足這些條件:
時,滿足要求的數對
的數目以及此時
的和。
我們發現這三種限制都可以按二進位制位決策,而且這三個限制都是一些上界或者下界,啟發我們使用數位DP,因此我們用傳統的狀態定義定義一個四維的狀態:令
為前
位中,滿足
不卡/卡上界,
不卡/卡上界,
不卡/卡下界的數對
數目,再類似定義
為滿足這麼多條件的所有
之和,就可以像正常數位DP一樣轉移了,時間複雜度為
。
以下是本人程式碼:
#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;
}