测试地址:储能表
做法: 本题需要用到数位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≤n−1,j≤m−1,(i xor j)≥k时,满足要求的数对
(
i
,
j
)
(i,j)
(i,j)的数目以及此时
i
x
o
r
j
i\space xor\space j
i xor j的和。
我们发现这三种限制都可以按二进制位决策,而且这三个限制都是一些上界或者下界,启发我们使用数位DP,因此我们用传统的状态定义定义一个四维的状态:令
c
n
t
(
d
,
0
/
1
,
0
/
1
,
0
/
1
)
cnt(d,0/1,0/1,0/1)
cnt(d,0/1,0/1,0/1)为前
d
d
d位中,满足
i
i
i不卡/卡上界,
j
j
j不卡/卡上界,
i
x
o
r
j
i\space xor\space j
i xor j不卡/卡下界的数对
(
i
,
j
)
(i,j)
(i,j)数目,再类似定义
s
u
m
(
d
,
0
/
1
,
0
/
1
,
0
/
1
)
sum(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
i xor j之和,就可以像正常数位DP一样转移了,时间复杂度为
O
(
T
⋅
64
⋅
2
5
)
O(T\cdot 64\cdot 2^5)
O(T⋅64⋅25)。
以下是本人代码:
#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;
}