JZOJ5682. 【GDSOI2018模拟4.22】数字

87 篇文章 0 订阅

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));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值