传送门:https://ac.nowcoder.com/acm/contest/887/H
这题一直忘了补。。。今天没比赛刚好补了
比赛的时候看见以为是撒高级数论计数题,然而怎么过了这么多人,最后没想到是数位DP。
我们知道对于位运算来说,当高位& >C 或者 高位^ <C,那么就一定能被算进答案里了。
所以从高位开始数位dp
dp[pos][dc][xc][sta][stb][az][bz]表示从最高位到pos+1位所有枚举的情况 的&值与C的大小为dc,^值与C的大小为xc, 当前数字是否等于a的高位的情况sta,b的stb,以及当前a是否为0的情况az,b的bz :这些状态下最后合法的方案数。
dc和xc 大于C就是2,等于C就是1,小于C就是0.
当前的x高位与a的高位相等sta=1,否则=0,stb一样。
当前x还是0,az=1,否则=0,bz一样。
边界情况是否合法就是 dc=2 || xc=0 而且az=0,bz=0,因为x,y都要>=1
#include<bits/stdc++.h>
using namespace std;
int aa,bb,cc;
int a[32],b[32],c[32];
long long dp[32][3][3][2][2][2][2];
long long ans;
bool vis[32][3][3][2][2][2][2];
inline void prework()
{
scanf("%d%d%d",&aa,&bb,&cc);
for(int i=0;i<=31;i++)
{
a[i]=(aa>>i)&1;
b[i]=(bb>>i)&1;
c[i]=(cc>>i)&1;
}
memset(dp,0,sizeof(dp));
memset(vis,false,sizeof(vis));
}
inline long long dfs(int pos,int dc,int xc,int sta,int stb,int az,int bz)
{
if(pos<0)
{
return (dc==2 || xc==0) && az==0 && bz==0;
}
if(vis[pos][dc][xc][sta][stb][az][bz])
return dp[pos][dc][xc][sta][stb][az][bz];
vis[pos][dc][xc][sta][stb][az][bz]=true;
int upx=!(sta && a[pos]==0);
int upy=!(stb && b[pos]==0);
int tdc,txc;
for(int i=0;i<=upx;i++)
for(int j=0;j<=upy;j++)
{
if(dc==1)
{
if((i&j)>c[pos])
tdc=2;
else if((i&j)<c[pos])
tdc=0;
else
tdc=1;
}
else
tdc=dc;
if(xc==1)
{
if((i^j)>c[pos])
txc=2;
else if((i^j)<c[pos])
txc=0;
else
txc=1;
}
else txc=xc;
dp[pos][dc][xc][sta][stb][az][bz]+=
dfs(pos-1,tdc,txc,sta && i==a[pos],stb && j==b[pos],az && i==0,bz && j==0);
}
return dp[pos][dc][xc][sta][stb][az][bz];
}
inline void mainwork()
{
ans=dfs(31,1,1,1,1,1,1);
}
inline void print()
{
printf("%lld\n",ans);
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
print();
}
return 0;
}
本文介绍了一种使用数位动态规划(数位DP)来解决原本看似复杂的高级数论计数问题的方法。通过分析题目,将问题转化为数位上的动态规划,详细解释了状态转移方程,并提供了完整的代码实现。
607

被折叠的 条评论
为什么被折叠?



