4513: [Sdoi2016]储能表

4513: [Sdoi2016]储能表

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 510   Solved: 266
[ 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

[ Submit][ Status][ Discuss]



数位dp。。。每次看到这种东西就头疼= =

f[i][j][k][a][b]:第i位数字xor为j,是否大于k,是否小于n,是否小于m,数字之和

g[i][j][k][a][b]:第i位数字xor为j,是否大于k,是否小于n,是否小于m,数字的数量

转移的话。。。瞎逼讨论咯= =

建议后三位压在一起,用0~7表示

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 62;
typedef long long LL;

int T,f[N][2][8],g[N][2][8],a[N],b[N],c[N],mi[N];
LL n,m,k,p;

int Add(const LL &x,const LL &y) {return (x + y) % p;}
int Mul(const LL &x,const LL &y) {return x*y%p;}
int Dec(const LL &x,const LL &y) {return (x - y + p) % p;}

void Work(LL x,int *A)
{
	for (int i = 0; i < N; i++)
		A[N-i-1] = (x&1LL),x >>= 1LL;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	cin >> T;
	while (T--)
	{
		scanf("%lld%lld%lld%lld",&n,&m,&k,&p);
		mi[N-1] = 1;
		for (int i = N-2; i >= 0; i--) mi[i] = Mul(mi[i+1],2);
		Work(--n,a); Work(--m,b); Work(k,c);
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		g[0][0][0] = 1;
		for (int i = 0; i < N - 1; i++)
			for (int j = 0; j < 2; j++)
				for (int l = 0; l < 8; l++)
				{
					if (!g[i][j][l]) continue;
					for (int A = 0; A < 2; A++)
						for (int B = 0; B < 2; B++)
						{
							if (!(l&2) && A > a[i+1]) continue;
							if (!(l&4) && B > b[i+1]) continue;
							int C = (A^B);
							if (!(l&1) && C < c[i+1]) continue;
							int f1,f2,f4,F;
							if ((l&1) || (!(l&1) && C > c[i+1])) f1 = 1; else f1 = 0;
							if ((l&2) || (!(l&2) && A < a[i+1])) f2 = 2; else f2 = 0;
							if ((l&4) || (!(l&4) && B < b[i+1])) f4 = 4; else f4 = 0;
							F = (f1|f2|f4);
							g[i+1][C][F] = Add(g[i+1][C][F],g[i][j][l]);
							int ADD = Mul(Mul(C,mi[i+1]),g[i][j][l]);
							f[i+1][C][F] = Add(f[i+1][C][F],Add(ADD,f[i][j][l]));
						}
				}
		int Ans = 0; k %= p;
		for (int i = 0; i < 2; i++)
			for (int j = 0; j < 8; j++)
			{
				Ans = Add(Ans,f[N-1][i][j]);
				Ans = Dec(Ans,Mul(g[N-1][i][j],k));
			}
		printf("%d\n",Ans);
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值