2019牛客暑期多校训练营(第七场)H(数位DP)

news/2024/7/10 4:07:50 标签: 数位DP, 优化, 位运算, 多个区间

满足要求的<x,y>有两种情况:
1.x&y >C;
2.x^y < C;
其中1<=x<=A, 1<=y<=B.
涉及到位运算,我们首先把A,B,C写成二进制,其次,要明确的一点就是,我们任找某个x,y,假设我们现在在进行&运算,并且是从高位到低位运算的,运算到二进制中的第k位时,如果此时k上的数字num, num>C[k] (2进制),那么就可以判定x&y>C了,而如果num<C[k],则x&y<C,如果num==C[k],那么就进行下一位的比较。^运算也同理。因此,根据这一点,我们就可以用数位DP来做了。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
typedef long long ll;
ll dp[40][3][3][2][2][2][2];	
//dp[pos][st1][st2][limit1][limit2][a][b]
//st1表示&运算的状态,2表示尚不确定与C的大小关系,1表示大于,0表示小于
//st2表示^运算的状态,2表示尚不确定与C的大小关系,1表示大于,0表示小于
//显然,我们最终要的状态是 st1==1||!st2
//a表示枚举到pos位,x是否为0,a==0表示x为0;
//b表示枚举到pos位,y是否为0,b==0表示y为0;
//这里要特别地加a,b,是因为如果把0算进去会多很多情况,但这些情况是我们不需要的。
//一开始我并不是再开了两维来记录这个a,b这个状态的,但是WA了,所以这两维是不可少的
//尚不清楚原理,但猜测是会将某些情况漏举了。反正空间也大不了多少,DP状态越具体的话越不容易错,就加上呗。
int A[40],B[40],C[40];

ll dfs(int pos,int st1,int st2,int a,int b,bool limit1,bool limit2)
{
	if(pos==-1)
	{
		if((st1==1||!st2)&&a&&b)	return 1;	
		return 0;
	}
	if(dp[pos][st1][st2][limit1][limit2][a][b]!=-1)	return dp[pos][st1][st2][limit1][limit2][a][b];
	ll ret=0;
	int up1= limit1? A[pos]:1;
	int up2= limit2? B[pos]:1;
	for(int i=0;i<=up1;++i)		//是在[1,A],[1,B]两个区间进行枚举的,所以再加一个循环就可以,可以推出n个区间就用n层循环
		for(int j=0;j<=up2;++j)
		{
			int t1=i&j, t2=i^j;
			int temp1=st1,temp2=st2;
			if(st1==2&&t1>C[pos])	temp1=1;		//我们只需要分析st1==2时的情况,因为st1=1,0这两种情况结果都已经确定了,不用再分析,下同
			else if(st1==2&&t1<C[pos])	temp1=0;
			if(st2==2&&t2<C[pos])	temp2=0;
			else if(st2==2&&t2>C[pos])	temp2=1;
			ret+=dfs(pos-1,temp1,temp2,a|i,b|j,limit1&&i==A[pos],limit2&&j==B[pos]);
		}
	dp[pos][st1][st2][limit1][limit2][a][b]=ret;
	return ret;
}

ll solve(ll a,ll b,ll c)
{
	int pos1=0,pos2=0,pos3=0,pos;
	memset(A,0,sizeof(A));
	memset(B,0,sizeof(B));
	memset(C,0,sizeof(C));
	while(a)	A[pos1++]=a%2, a/=2;
	while(b)	B[pos2++]=b%2, b/=2;
	while(c)	C[pos3++]=c%2, c/=2;
	memset(dp,-1,sizeof(dp));
	pos=max(pos1,max(pos2,pos3));
	return dfs(--pos,2,2,0,0,1,1);
}

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		ll a,b,c;
		cin>>a>>b>>c;
		cout<<solve(a,b,c)<<endl;
	}
	return 0;
}

小结:
1.在n个区间里面枚举,就用n层循环DP;
2.a,b,这两维要加上,状态是越具体越不容易出错的(个人经验)
3.本来,dp数组里面是没有[limit1][limit2]的,是在dfs里加了个! limit1&&! limit2的判断,结果TLE!!!加上去以后5MS秒过!!!(神奇)所以以后或许可以用这一定来优化一下时间复杂度。


http://www.niftyadmin.cn/n/942999.html

相关文章

记JSOI2015第一轮

天国的标准分你可安好 打错变量名崩崩崩 260变60的感觉你可知晓 从RANK2直接变成RANK42 第二轮不切个标准分真对不起第一轮傻逼的自己

HDU2067,小兔的棋盘(卡特兰数)

一开始用DFS求出方案数&#xff0c;发现答案是2*C(2n,n)/(n1)&#xff0c;然而在计算34,35的时候过程爆数据&#xff0c;过不了。看了讨论区才发现一种新的东西——卡特兰数。。 关于卡特兰数&#xff0c;可阅读博客&#xff1a; https://blog.csdn.net/Hackbuteer1/article/de…

记WC2015

既然写了JSOI2015就顺便写写WC的那些事吧 到学军中学第一印象&#xff1a;竟然每人都送书包诶。这食堂饭菜真不错诶。 然后我就什么也记不起来了 第二天听完IOI的题目选讲就睡过去了。后面的TopTree完全听不懂。 然后听到了“我们来讲些轻松愉快的东西吧”这句话。然后果断…

HDU2068,RPG的错排(错排+组合数)

枚举猜对(n1)/2, (n1)/21, … , n-3, 个人的情况&#xff0c;用组合数错排公式求出&#xff0c;原理就是设你猜对了k个人&#xff0c;先从这n个人里面挑出k个人出来&#xff0c;就是C(n,k)&#xff0c;在对剩余的n-k个人错排&#xff0c;就是(n-k-1) * (a[n-k-1] a[n-k-2])&am…

UVA 10635,Prince and Princess(LCS→LIS优化)

原本是用LCS优化过的&#xff0c;后来看到有些大牛都是10MS就过的&#xff0c;觉得甚是神奇&#xff0c;就去查了一下&#xff0c;原来可以将LCS转为LIS&#xff0c;而LIS可以有NlogN的算法。 关于LIS的NlogN的做法&#xff0c;可看博客&#xff1a; https://blog.csdn.net/shu…

bzoj 3873: [Ahoi2014]拼图

Description JYY最近迷上了拼图游戏。作为一个计算机科学家&#xff0c;JYY有一套黑白色的拼图&#xff0c;他希望通过合理的拼接&#xff0c;使得拼出的最终图案中&#xff0c;能包含面积最大的全白色子矩形。JYY一共有S块拼图&#xff0c;并且由1到S编号。编号为i的拼图是一个…

bzoj 3671: [Noi2014]随机数生成器

Description Input 第1行包含5个整数&#xff0c;依次为 x_0,a,b,c,d &#xff0c;描述小H采用的随机数生成算法所需的随机种子。第2行包含三个整数 N,M,Q &#xff0c;表示小H希望生成一个1到 NM 的排列来填入她 N 行 M 列的棋盘&#xff0c;并且小H在初始的 NM 次交换操作后&…

POJ3254,Corn Fields(状压DP)

题意:农夫有一块地&#xff0c;被划分为m行n列大小相等的格子&#xff0c;其中一些格子是可以种植作物的&#xff08;用1标记&#xff09;&#xff0c;其他格子则不能种植&#xff08;用0标记&#xff09;&#xff0c;并且要求相邻格子不能同时都有种植作物。现在输入数据给出这…