2019牛客暑期多校训练营(第七场) H Pair 数位DP 维护两个数 异或 与 关系

ll dp[33][2][2][2][2][3][3];

表示: A和B 填到第i位,(前面i-1位已经填完,现在开始填第i位),前面i-1位是否达到A的上界(la),是否达到B的上界(lb);

A的第i-1位是否是前导0(ifa0),B的第i-1位是否是前导0(ifb0) ,  前i-1位填的结果,A & B > C 则为2,A & B == C 则为1,A & B < C 则为0.(比较的是AB前i-1位填的结果与C的前i-1位比较),最后是:

前i-1位填的结果A xor B > C 则为2,A xor  B == C 则为1,A xor  B < C 则为0.

 

AB前导0的维护是去掉最后取成0的情况,(因为题目要求x,y大于等于1,无法取到0))

维护上界届la,lb是为了使A<=NA,B<=NB,  

维护AND,OR 是为了满足 A & B > C 或者A xor B < C

 

剩下的就是简单的DP状态转移了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define re register
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt=1;
void init(int n){cnt=1;for(int i=0;i<=n;i++)head[i]=0;}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
//a&b > c  || a^b < c
int da[33],db[33],dc[33],sc,sa,sb,A,B,C;
ll dp[33][2][2][2][2][3][3];
ll dfs(int len,bool la,bool lb,bool ifa0,bool ifb0,int AND,int OR)
{
//	cout<< len <<" "<<la<<" "<<lb<<" "<<AND<<" "<<OR<< "  =  "<< dp[len][la][lb][ifa0][ifb0][AND][OR]<<endl;
	if(len==0 )return ( AND == 2 || OR == 0) && !ifa0 && !ifb0;
	
	if(~dp[len][la][lb][ifa0][ifb0][AND][OR])return dp[len][la][lb][ifa0][ifb0][AND][OR];
	ll ans=0;
	int upa=la?da[len]:1,upb=lb?db[len]:1;
	for(int i=0;i<=upa;i++)//a
		for(int j=0;j<=upb;j++)//b
		{
			int q=i&j,w=i^j,CAND=AND,COR=OR;
			if(AND == 1 )
			{
				if(q > dc[len])CAND=2;
				else if(q == dc[len])CAND=1;
				else CAND = 0;
			}
			if(OR == 1 )
			{
				if(w > dc[len])COR=2;
				else if(w == dc[len])COR=1;
				else COR = 0;
			}
			
		//	cout<<"      ====          "<<i<<"  "<<j<<"   "<< AND<<"  - "<<OR <<endl; 
			if(CAND == 0 && COR == 2) continue;//不可能满足条件了
			ans+=dfs(len-1,la&&i==upa,lb&&j==upb,ifa0&&i==0,ifb0&&j==0,CAND,COR);
		}
	//cout<< len <<" "<<la<<" "<<lb<<" "<<AND<<" "<<OR<< "  =  "<< dp[len][la][lb][AND][OR]<<endl;
	//cout<<"---------------------------------   "<<ans<<endl;
	return dp[len][la][lb][ifa0][ifb0][AND][OR]=ans;
}
ll cal()
{
	sa=sb=sc=0;
	memset(da,0,sizeof(da));
	memset(db,0,sizeof(db));
	memset(dc,0,sizeof(dc));
	int fAND = C>max(A,B)?0:1;
	while(A){da[++sa]=A%2;A/=2;}
	while(B){db[++sb]=B%2;B/=2;} 
	while(C){dc[++sc]=C%2;C/=2;} 
	int fOR = sc>max(sa,sb)?0:1;
	memset(dp,-1,sizeof(dp));
	return dfs(max(sa,sb),true,true,true,true,fAND,fOR);
}
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int T;
  	cin>>T;
  	while(T--)
  	{
  		cin>>A>>B>>C;
		cout<<cal()<<endl;
	}
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值