【Luogu】P4067储能表(数位DP)

  题目链接

  好的

  看到这题之后我一直在想反演,然后想不出来,一度以为自己脑子有问题

  然后我脑子真的有问题,这题tm根本就不是反演

  设f[i][j][k][l]表示现在已经DP到从高位往低数的第i位,有没有碰到n的上界,有没有碰到m的上界,有没有碰到k的上界

  然后记忆化DFS搞一搞,把方案数和异或和都记录下来,最后减掉输出

  

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cstdlib>
#include<algorithm>
#define maxn 50
using namespace std;
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

long long n,m,e,mod;

struct Ans{
    long long xum,sum;
}f[100][2][2][2];
bool vis[100][2][2][2];

long long Maxi;

void dfs(long long len,long long nown,long long nowm,long long nowk){
    if(len>Maxi){
        f[len][nown][nowm][nowk]=(Ans){0,1};
        return;
    }
    if(vis[len][nown][nowm][nowk])    return;
    Ans &ret=f[len][nown][nowm][nowk];
    vis[len][nown][nowm][nowk]=1;
    long long mn=(n>>(Maxi-len))&1,mm=(m>>(Maxi-len))&1,mk=(e>>(Maxi-len))&1;
    for(long long i=0;i<=(nown?mn:1);++i)
        for(long long j=0;j<=(nowm?mm:1);++j){
            if(nowk&&(i^j)<mk)    continue;
            dfs(len+1,nown&&(i==mn),nowm&&(j==mm),nowk&&((i^j)==mk));
            Ans now=f[len+1][nown&&(i==mn)][nowm&&(j==mm)][nowk&&(i^j)==mk];
            ret.sum=(ret.sum+now.sum)%mod;
            ret.xum=(ret.xum+(1ll<<(Maxi-len))*(i^j)%mod*now.sum+now.xum)%mod;
        }
    return;
}


int main(){
    long long T=read();
    while(T--){
        n=read()-1;m=read()-1;e=read();mod=read();
        memset(vis,0,sizeof(vis));    Maxi=0;
        memset(f,0,sizeof(f));
        long long nn=n,nm=m,ne=e;long long cnt=0;
        while(nn){    cnt++;    nn>>=1;}
        Maxi=max(Maxi,cnt);    cnt=0;
        while(nm){    cnt++;    nm>>=1;}
        Maxi=max(Maxi,cnt);    cnt=0;
        while(ne){    cnt++;    ne>>=1;}
        Maxi=max(Maxi,cnt);
        dfs(1,1,1,1);
        Ans now=f[1][1][1][1];
        long long ans=(now.xum%mod-(e%mod)*now.sum%mod+mod)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

 

posted @ 2018-04-19 14:25  Konoset  阅读(175)  评论(0编辑  收藏  举报