[SDOI2016]儲能表

一、題目

點此看題

二、解法

不要把這道題從圖形的角度考慮,我們考慮它的本質就是求:
i=0n1j=0m1max((i xor j)k,0)\sum_{i=0}^{n-1}\sum_{j=0}^{m-1}\max((i\space xor\space j)-k,0)可以考慮數位dpdp,只不過特殊的是我們要同時維護兩個數,而且數位指的是二進制位,設f[i][a][b][c]f[i][a][b][c]dpdp到了二進制的第ii位,是否頂到nn的上界,是否頂到mm的上界,是否頂到kk的上界。

轉移就枚舉兩個數當前位選什麼,然後驗證a,b,ca,b,c,這裏還要維護一個個數g[i][a][b][c]g[i][a][b][c],轉移時個數直接累加,答案就加上當前位的貢獻在乘上個數,剩下的就是數位dpdp日常操作了,貼個代碼qwqqwq

#include <cstdio>
#include <cstring>
#define int long long
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int T,n,m,k,p;
int f[62][2][2][2],g[62][2][2][2];
void init()
{
    n=read();
    m=read();
    k=read();
    p=read();
    memset(f,0,sizeof f);
    memset(g,0,sizeof g);
    g[61][1][1][1]=1;
    for(int i=60; i>=0; i--)
    {
        int x=(n>>i)&1,y=(m>>i)&1,z=(k>>i)&1;
        for(int a=0; a<2; a++)
            for(int b=0; b<2; b++)
                for(int c=0; c<2; c++)
                    if(f[i+1][a][b][c] || g[i+1][a][b][c])
                        for(int xx=0; xx<2; xx++)
                            for(int yy=0; yy<2; yy++)
                            {
                                int zz=xx^yy;
                                if((a && xx>x) || (b && yy>y) || (c && zz<z)) continue;
                                int aa=(a && xx==x),bb=(b && yy==y),cc=(c && zz==z);
                                g[i][aa][bb][cc]=(g[i][aa][bb][cc]+g[i+1][a][b][c])%p;
                                f[i][aa][bb][cc]=(f[i][aa][bb][cc]+f[i+1][a][b][c]+(zz-z+p)%p*((1ll<<i)%p)%p*g[i+1][a][b][c]%p)%p;
                            }
    }
    printf("%lld\n",f[0][0][0][0]);
}
signed main()
{
    T=read();
    while(T--)
    {
        init();
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章