BZOJ4513 SDOI2016储能表(数位dp)
如果n、m、k都是2的幂次方,答案非常好统计。于是容易想到数位dp,考虑每一位是否卡限制即可,即设f[i][0/1][0/1][0/1]为第i位是/否卡n、m、k的限制时,之前的位的总贡献;g[i][0/1][0/1][0/1]为第i位是/否卡n、m、k的限制时,之前的位的方案数。为了方便可以改为统计小于k的贡献再减去。
莫名其妙的搞错了很多地方,简直调一年,不知道在干啥。
人丑常数大,根本没办法。
(突然发现以前大部分数位dp都是直接按位计数就搞出来了……这个题应该也行。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 64 ll read() { ll x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int T,p,f[N][2][2][2],g[N][2][2][2],a[N],b[N],c[N],q[N]; ll n,m,k; void inc(int &x,int y,int p){x+=y;if (x>=p) x-=p;} void calc(ll n,ll m,ll k,int p) { memset(f,0,sizeof(f));memset(g,0,sizeof(g)); int t=-1;ll x=max(max(n,m),k); while (x) t++,x>>=1; for (int i=0;i<=t;i++) a[i]=(n&(1ll<<i))>0; for (int i=0;i<=t;i++) b[i]=(m&(1ll<<i))>0; for (int i=0;i<=t;i++) c[i]=(k&(1ll<<i))>0; g[t+1][1][1][1]=1; for (register int i=t;~i;i--) for (register int x=0;x<=1;x++) for (register int y=0;y<=1;y++) for (register int z=0;z<=1;z++) for (register int u=x;u<=1;u++) for (register int v=y;v<=1;v++) for (register int w=z;w<=1;w++) { int t=(!w|c[i]&z)?(((!u|a[i]&x)&(!v|b[i]^y))+((!v|b[i]&y)&(!u|a[i]^x))):0; inc(f[i][x][y][z],1ll*q[i]*g[i+1][u][v][w]%p*t%p,p); if (!w|c[i]^z) t+=(((!u|a[i]&x)&(!v|b[i]&y))+((!u|a[i]^x)&(!v|b[i]^y))); inc(f[i][x][y][z],1ll*f[i+1][u][v][w]*t%p,p); inc(g[i][x][y][z],1ll*g[i+1][u][v][w]*t%p,p); } } int main() { #ifndef ONLINE_JUDGE freopen("bzoj4513.in","r",stdin); freopen("bzoj4513.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif T=read(); while (T--) { n=read()-1,m=read()-1,k=read(),p=read(); q[0]=1;for (int i=1;i<=63;i++) q[i]=(q[i-1]<<1)%p; int ans=0; calc(n,m,max(n,m)<<1,p); for (int x=0;x<=1;x++) for (int y=0;y<=1;y++) for (int z=0;z<=1;z++) inc(ans,f[0][x][y][z],p),inc(ans,p-1ll*k%p*g[0][x][y][z]%p,p); calc(n,m,k,p); for (int x=0;x<=1;x++) for (int y=0;y<=1;y++) for (int z=0;z<=1;z++) inc(ans,p-f[0][x][y][z],p),inc(ans,1ll*k%p*g[0][x][y][z]%p,p); cout<<ans<<endl; } return 0; }