FZU2042(数位dp)

好题!难题!

题意:

给出两个区间,现在任取区间的两个数(每个区间取一个),假设第一个区间取数i,第二个区间取数j,判断满足i^j>e的所有i^j的和,其中e是给定的数。

题解:
数位dp,设计这样的状态dp[i][j][k][t] 第i位时满足条件的个数,状态不好解释直解看代码。把所有的 位数弄成2进制的形式,因为2进制只有01在处理时有一些技巧,具体看代码。

注意一个细节:如果位数的存储从1开始,在代码中移位时要将pos-1,如果从0开始存储则不用。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<math.h>
using namespace std;
typedef long long lld;
#define oo 0x3f3f3f3f
const lld MOD=1000000007;
#define digit 66
#define maxn 2525
struct Dp
{
    lld cnt;
    lld res;
    Dp(){cnt=res=0;}
    Dp(lld cnt_,lld res_){ cnt=cnt_; res=res_; }
};
Dp dp[digit][2][2][2];
int bita[digit],bitb[digit],bitc[digit];

Dp dfs(int pos,int fa,int fb,int fc)
{
    if(pos<1) return !fc ? Dp(1,0) : Dp(0,0); //因为要大于e所以异或的最后一位必须要大于第三个范围的最后一位即:fc==0
    if(dp[pos][fa][fb][fc].cnt!=-1&&dp[pos][fa][fb][fc].res!=-1) return dp[pos][fa][fb][fc];
    int lasta=fa?bita[pos]:1;
    int lastb=fb?bitb[pos]:1;
    int lastc=fc?bitc[pos]:-1;
    Dp res=Dp(0,0);
    for(int i=0;i<=lasta;i++)
    {
        for(int j=0;j<=lastb;j++)
        {
            int t=i^j;
            if(t>=lastc)
            {
                Dp temp=dfs(pos-1,fa&&i==lasta,fb&&j==lastb,fc&&t==lastc);
                res.cnt=(res.cnt+temp.cnt)%MOD;
                res.res=((res.res+t*(1ll<<(pos-1))%MOD*temp.cnt%MOD)%MOD+temp.res)%MOD;
            }
        }
    }
    dp[pos][fa][fb][fc]=res;
    return res;
}

//转化成二进制并且返回长度
int GetBit(lld x,int a[])
{
    int len=0;
    while(x)
    {
        a[++len]=x%2;
        x/=2;
    }
    return len;
}

lld GetAns(lld a,lld b,lld c)
{
    for(int i=0;i<digit;i++)
    {
        for(int j=0;j<2;j++)
        {
            for(int k=0;k<2;k++)
            {
                for(int t=0;t<2;t++)
                {
                    dp[i][j][k][t]=Dp(-1,-1);
                }
            }
        }
    }
    int lena=GetBit(a,bita);
    int lenb=GetBit(b,bitb);
    int lenc=GetBit(c,bitc);
    int len=max(lena,max(lenb,lenc));
    while(lena<len) bita[++lena]=0;
    while(lenb<len) bitb[++lenb]=0;
    while(lenc<len) bitc[++lenc]=0;
    return (dfs(len,1,1,1).res+MOD)%MOD;
}

int main()
{
    int T;
    lld a,b,c,d,e;
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++)
    {
        cin>>a>>b>>c>>d>>e;
        lld t1=GetAns(b,d,e);
        lld t2=GetAns(b,c-1,e);
        lld t3=GetAns(a-1,d,e);
        lld t4=GetAns(a-1,c-1,e);
        printf("Case %d: ",cas);
        cout<<((t1-t2+MOD)%MOD-t3+t4+MOD)%MOD<<endl;
    }
	return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值