洛谷CF809C Find a car(数位DP)

洛谷题目传送门

通过瞪眼法发现,\(a_{i,j}=(i-1)\text{ xor }(j-1)+1\)

二维差分一下,我们只要能求\(\sum\limits_{i=0}^x\sum\limits_{j=0}^y[i\text{ xor }j\le k]\)就好了。

比较套路的数位DP。

从高位往低位做,设\(f[t][0/1][0/1][0/1]\)表示到第\(t\)位,\(i,j,i\text{ xor }j\)已确定的值是否卡到\(x,y,k\)\(t\)位的上界的方案数和权值和。

每一位的转移都是一个小讨论:如果之前卡到上界,这一位可以接着卡,或者如果这一位的上界是\(1\),就可以填\(0\)转移到不卡上界。如果之前不卡了,那么这一位随便选。

注意到最开始的式子里有一个\(+1\),所以要输出\(\sum\)权值和+方案数。

下面的代码使用了压位和define可能会比较丑

#include<bits/stdc++.h>
#define R register int
using namespace std;
const int YL=1e9+7;
int t,fc[8],fs[8],gc[8],gs[8];
inline int in(){R x;scanf("%d",&x);return x;}
inline void M(R&x){if(x>=YL)x-=YL;}
#define T(x,u,v) if(i>1||w==x)Trans(i>>1,li,u,v,i>1?w^x:w)
void Trans(R i,R li,R u,R v,R w){//暴搜转移
    if(!i){
        if(w)gs[v]=(gs[v]+fc[u]*(long long)t)%YL;
        return M(gc[v]+=fc[u]),M(gs[v]+=fs[u]);
    }
    if(i&li){T(0,u,v|i);T(1,u,v);}//讨论开始
    else T(0,u,v);
    T(0,u|i,v|i);
    T(1,u|i,v|i);
}
int Dp(R n,R m,R k){
    if(n<0||m<0)return 0;
    memset(fc,0,32);fc[0]=1;
    memset(fs,0,32);
    for(t=1<<30;t;t>>=1){
        Trans(4,!!(n&t)*4|!!(m&t)*2|!!(k&t),0,0,0);
        memcpy(fc,gc,32),memset(gc,0,32);
        memcpy(fs,gs,32),memset(gs,0,32);
    }
    R s=0;
    for(R i=0;i<8;++i)M(s+=fs[i]),M(s+=fc[i]);
    return s;
}
int main(){
    for(R q=in();q--;){
        R x1=in()-2,y1=in()-2,x2=in()-1,y2=in()-1,k=in()-1;
        cout<<((Dp(x2,y2,k)-Dp(x1,y2,k)-Dp(x2,y1,k)+Dp(x1,y1,k))%YL+YL)%YL<<endl;
    }
    return 0;
}
posted @ 2019-03-17 17:17  Flash_Hu  阅读(373)  评论(0编辑  收藏  举报