Description
NiroBC姐姐想要创造两个数字x和y,它们需要满足x∨y=T,且Lx≤x≤Rx ,Ly≤y≤Ry,NiroBC姐姐想知道x∧y有多少种可能的不同的取值。若有多组 (x,y)有相同的x∧y的取值,则只算一次。
注:
•其中∨表示按位取或,C/C++中写作|,Pascal中写作or。
•其中∧表示按位取与,C/C++中写作&,Pascal中写作and。
Input
一行,五个非负整数T,Lx,Rx,Ly,Ry。
Output
一行,一个整数,表示答案。
Sample Input
11 3 10 8 13
Sample Output
7
题解
设W=x&y
分别对每一位讨论。
W=0,T=0,那么x,y都必须为0
W=0,T=1,那么x为1,y为0,或者y为1,x为0
W=1,T=0,这种情况不合法,
W=1,T=1,x,y都必须为1
但题目还有一个x,y的上下界限制,
按照数位DP的思想,可以将状态分为9种情况,
x压下界,在上下界之间,压上界,y同理。
从高位往低位枚举,
对于每一个位置的每种状态进行转移,
具体就是枚举这个位置填什么,填了之后x,y的状态是什么,就转移到下一个状态。
code
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#define G getchar
#define ll long long
#define N 1000003
using namespace std;
ll T,lx,ly,rx,ry,z[63],f[63][512];
ll dg(int pos,ll s)
{
if(pos<0)return 1;
if(f[pos][s])return f[pos][s];
ll ans=0;
for(int now=0;now<=((T&z[pos])>0);now++)
{
int S=0;
for(int x=0;x<=1;x++)
for(int y=0;y<=1;y++)
if((x|y)==((T&z[pos])>0) && (x&y)==now)
{
ll s1,s2;
int t1,t2;
for(int t=0;t<9;t++)
if(s>>t&1)
{
t1=t%3;t2=t/3;
if(t1==0)s1=lx>>pos+1;else//压下界
if(t1==1)s1=rx>>pos+1;else//压上界
s1=(lx>>pos+1)+1;//中间
if(t2==0)s2=ly>>pos+1;else//压下界
if(t2==1)s2=ry>>pos+1;else//压上界
s2=(ly>>pos+1)+1;//中间
s1<<=1;s2<<=1;s1|=x;s2|=y;
if(s1>(rx>>pos) || s2>(ry>>pos) || s1<(lx>>pos) || s2<(ly>>pos))continue;
if(s1==(lx>>pos))t1=0;else//压下界
if(s1==(rx>>pos))t1=1;else//压上界
t1=2;//中间
if(s2==(ly>>pos))t2=0;else//压下界
if(s2==(ry>>pos))t2=1;else//压上界
t2=2;//中间
S|=z[t1+t2*3];
}
}
if(S)ans+=dg(pos-1,S);
}
return f[pos][s]=ans;
}
int main()
{
z[0]=1;
for(int i=1;i<63;i++)
z[i]=z[i-1]<<1;
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
scanf("%lld%lld%lld%lld%lld",&T,&lx,&rx,&ly,&ry);
printf("%lld\n",dg(61,1));
}