bzoj4513【SDOI2016】储能表

来源:互联网 发布:精雕雕刻机编程教程 编辑:程序博客网 时间:2024/04/29 07:55

4513: [Sdoi2016]储能表

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 340  Solved: 182
[Submit][Status][Discuss]

Description

有一个 n 行 m 列的表格,行从 0 到 n−1 编号,列从 0 到 m−1 编号。每个格子都储存着能量。最初,第 i 行第 j 列的格子储存着 (i xor j) 点能量。所以,整个表格储存的总能量是,

随着时间的推移,格子中的能量会渐渐减少。一个时间单位,每个格子中的能量都会减少 1。显然,一个格子的能量减少到 0 之后就不会再减少了。
也就是说,k 个时间单位后,整个表格储存的总能量是,
给出一个表格,求 k 个时间单位后它储存的总能量。
由于总能量可能较大,输出时对 p 取模。

Input

第一行一个整数 T,表示数据组数。接下来 T 行,每行四个整数 n、m、k、p。

Output

 共 T 行,每行一个数,表示总能量对 p 取模后的结果

Sample Input

3
2 2 0 100
3 3 0 100
3 3 1 100

Sample Output

2
12
6

HINT

 T=5000,n≤10^18,m≤10^18,k≤10^18,p≤10^9

Source

鸣谢Menci上传




灰常麻烦的数位DP(二进制数位)

f[i][1/0][1/0][1/0]表示DP到第i位,等于/小于n,等于/小于m,等于/大于k的数对的个数。

g[i][1/0][1/0][1/0]表示DP到第i位,等于/小于n,等于/小于m,等于/大于k的数对异或值的和。

然后从高位到低位DP转移。

为什么要从高到低呢?因为只有这样才可以限制是否达到上界或下界。




#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<algorithm>#define F(i,j,n) for(int i=j;i<=n;i++)#define D(i,j,n) for(int i=j;i>=n;i--)#define ll long longusing namespace std;ll t,n,m,k,p,ans,f[100][2][2][2],g[100][2][2][2],bin[100];inline ll read(){ll x=0,f=1;char ch=getchar();while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}int main(){t=read();while (t--){n=read()-1;m=read()-1;k=read();p=read();bin[0]=1;F(i,1,60) bin[i]=bin[i-1]*2%p;ans=0;memset(f,0,sizeof(f));memset(g,0,sizeof(g));f[0][1][1][1]=1;F(i,0,60) F(a,0,1) F(b,0,1) F(c,0,1) if (f[i][a][b][c]){ll tn=n&(1ll<<(60-i))?1:0,tm=m&(1ll<<(60-i))?1:0,tk=k&(1ll<<(60-i))?1:0;F(x,0,1) if (!a||x<=tn) F(y,0,1) if (!b||y<=tm){int z=x^y;if (c&&z<tk) continue;int ta=(a&&x==tn)?1:0,tb=(b&&y==tm)?1:0,tc=(c&&z==tk)?1:0;(f[i+1][ta][tb][tc]+=f[i][a][b][c])%=p;(g[i+1][ta][tb][tc]+=g[i][a][b][c])%=p;if (z) (g[i+1][ta][tb][tc]+=bin[60-i]*f[i][a][b][c]%p)%=p;}}F(a,0,1) F(b,0,1) F(c,0,1) ans=((ans+g[61][a][b][c]-k%p*f[61][a][b][c]%p)%p+p)%p;printf("%lld\n",ans);}return 0;}


1 0
原创粉丝点击