[SDOI2016] 储能表

[SDOI2016]储能表

题目描述

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

\[\sum_{i=0}^{n-1} \sum_{j=0}^{m-1} (i \mathrm{xor} j) \]

随着时间的推移,格子中的能量会渐渐减少。一个时间单位,每个格子中的能量都会减少 1。显然,一个格子的能量减少到 0 之后就不会再减少了。

也就是说,k 个时间单位后,整个表格储存的总能量是

\[\sum_{i=0}^{n-1} \sum_{j=0}^{m-1} \mathrm{max} ((i \mathrm{xor} j)-k,0) \]

给出一个表格,求 k 个时间单位后它储存的总能量。

由于总能量可能较大,输出时对 p 取模。

输入格式

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

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

2
12
6

提示

测试点 \(1 \sim 2\)\(T = 5000\)\(n \leq 100\)\(m \leq 100\)\(k \leq 100\)\(p \leq 10 ^ 9\)

测试点 \(3\)\(T = 5000\)\(n \leq 10 ^ {18}\)\(m \leq 10 ^ {18}\)\(k = 0\)\(p \leq 10 ^ 9\)

测试点 \(4\)\(T = 5000\)\(n \leq 10 ^ {18}\)\(m \leq 10 ^ {18}\)\(k = 1\)\(p \leq 10 ^ 9\)

测试点 \(5\)\(T = 5000\)\(n \leq 10\)\(m \leq 10 ^ {18}\)\(k \leq 10\)\(p \leq 10 ^ 9\)

测试点 \(6\)\(T = 1\)\(n \leq 10 ^ 5\)\(m \leq 10 ^ {18}\)\(k \leq 10 ^ 5\)\(p \leq 10 ^ 9\)

测试点 \(7\)\(T = 1\)\(n \leq 10 ^ {18}\)\(m \leq 10 ^ {18}\)\(k \leq 10 ^ {18}\)\(p \leq 10 ^ 9\)

测试点 \(8\)\(T = 100\)\(n\leq 10 ^ {18}\)\(m \leq 10 ^ {18}\)\(k \leq 10 ^ {18}\)\(p \leq 10 ^ 9\)

测试点 \(9 \sim 10\)\(T = 5000\)\(n \leq 10 ^ {18}\)\(m \leq 10 ^ {18}\)\(k \leq 10 ^ {18}\)\(p \leq 10 ^ 9\)

略微有点板的数位dp.不理解为什么是紫色。

\(-k\) 尝试拆开,如果 \(i\oplus j\) 小于等于 \(k\) 的那就不用看了,剩下的如果 \(i^j\) 大于等于 \(k\) 的有 \(cnt\) 个,他们的和为 \(ans\),那么答案就是 \(ans-cnt\times k\)

那怎么数位 dp 呢?定义 \(dp_{i,0/1,0/1,0/1}\) 为前 \(i\) 位,是否有顶到 \(n/m\) 的上界和 \(k\) 的下界时的情况。那么转移就很简单了。枚举原式中 \(i\)\(j\) 在这一位的四种填法,然后递归到下一位,若下一位答案为 \((cnt,ans)\),那么这一位就是所有 \((cnt,ans+cnt\times 2^i)\) 之和。这应该很好理解了。

#include<bits/stdc++.h>
int t,P;
long long n,m,k;
struct node{
	int cnt,ans;
	node operator+(const node&n)const{
		return (node){(cnt+n.cnt)%P,(ans+n.ans)%P};
	}
}dp[65][2][2][2],ret,dw;
node dfs(int x,int an,int am,int ak);
node calc(int x,int an,int am,int ak,int p,int q);
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld%lld%lld%d",&n,&m,&k,&P);
		memset(dp,-1,sizeof(dp));
//		printf("%d ",dp[0][0][0][0].cnt);
		--n,--m;
		ret=dfs(60,0,0,0);
//		printf("%d %d\n",ret.cnt,ret.ans);
		printf("%d\n",(ret.ans-k%P*ret.cnt%P+P)%P);
	}
	return 0;
}
node dfs(int x,int an,int am,int ak)
{
//	if(x<1)
//		printf("%d %d %d %d\n",x,an,am,ak);
	if(!~x)
//		printf("%d %d\n",an,am);
		return (node){1,0};
	if(~dp[x][an][am][ak].cnt)
		return dp[x][an][am][ak];
	node ret=dw;
	ret=ret+calc(x,an,am,ak,0,0);
	ret=ret+calc(x,an,am,ak,0,1);
	ret=ret+calc(x,an,am,ak,1,0);
	ret=ret+calc(x,an,am,ak,1,1);
//	if(x<=1)
//		printf("%d %d %d %d %d %d\n",x,an,am,ak,ret.cnt,ret.ans);
	return dp[x][an][am][ak]=ret;
}
node calc(int x,int an,int am,int ak,int p,int q)
{
	int nx=n>>x&1,mx=m>>x&1,kx=k>>x&1;
	if(!an&&p>nx)
		return dw;
	if(!am&&q>mx)
		return dw;
	if(!ak&&(p^q)<kx)
		return dw;
	an|=p<nx;
	am|=q<mx;
	ak|=(p^q)>kx;
	node ret=dfs(x-1,an,am,ak);
	(ret.ans+=ret.cnt*(((p^q*1LL)<<x)%P)%P)%=P;
	return ret;
}
posted @ 2023-01-11 20:00  灰鲭鲨  阅读(55)  评论(0)    收藏  举报