【CF809C】Find a car(动态规划)

【CF809C】Find a car(动态规划)

题面

洛谷
CF
有一个无穷大的矩阵,第\(i\)行第\(j\)列的数是\((i-1)xor(j-1)+1\)\(q\)次询问,每次询问一个矩形内数小于等于\(k\)的数的和。

题解

询问等价于\(\sum_{i=l}^r\sum_{j=L}^R [i\oplus j\le k]i\oplus j\)
把询问拆分成四个从\(1\)开始的东西,即\([1..r,1..R],[1..l-1,1..R],[1..r,1..L-1],[1..l-1,1..L-1]\)
那么就可以大力数位\(dp\)了,设\(f[i][0/1][0/1][0/1]\)表示\(i\)是否卡在界上,\(j\)是否卡在界上,\(i\oplus j\)是否卡在界上,需要同时记录方案数和和。
大力转移一下就好了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MOD 1000000007
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int f[32][2][2][2],g[32][2][2][2];
int Calc(int L,int R,int K)
{
    if(L<0||R<0)return 0;
    memset(f,0,sizeof(f));memset(g,0,sizeof(g));f[31][0][0][0]=1;
    for(int i=30;~i;--i)
        for(int a=0;a<=1;++a)
            for(int b=0;b<=1;++b)
                for(int k=0;k<=1;++k)
                {
                    if(!f[i+1][a][b][k])continue;
                    for(int A=0;A<=1;++A)
                        for(int B=0;B<=1;++B)
                        {
                            if(!a&&A&&!(L&(1<<i)))continue;
                            if(!b&&B&&!(R&(1<<i)))continue;
                            if(!k&&!(K&(1<<i))&&(A^B))continue;
                            int na=a,nb=b,nk=k;
                            if(!A&&(L&(1<<i)))na|=1;
                            if(!B&&(R&(1<<i)))nb|=1;
                            if(!(A^B)&&(K&(1<<i)))nk|=1;
                            add(f[i][na][nb][nk],f[i+1][a][b][k]);
                            add(g[i][na][nb][nk],g[i+1][a][b][k]);
                            if(A^B)add(g[i][na][nb][nk],1ll*(1<<i)*f[i+1][a][b][k]%MOD);
                        }
                }
    int ret=0;
    for(int a=0;a<=1;++a)
        for(int b=0;b<=1;++b)
            for(int k=0;k<=1;++k)
                add(ret,g[0][a][b][k]),add(ret,f[0][a][b][k]);
    return ret;
}
int main()
{
    int Q=read();
    while(Q--)
    {
        int x1=read()-1,x2=read()-1,y1=read()-1,y2=read()-1,k=read()-1;
        int ans1=(Calc(y1,y2,k)+Calc(x1-1,x2-1,k))%MOD;
        int ans2=(Calc(x1-1,y2,k)+Calc(y1,x2-1,k))%MOD;
        int ans=(ans1+MOD-ans2)%MOD;
        printf("%d\n",ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/cjyyb/p/10473561.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值