[SDOI2016] 储能表

[SDOI2016]储能表

题目描述

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

i=0n1j=0m1(ixorj)

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

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

i=0n1j=0m1max((ixorj)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

提示

测试点 12T=5000n100m100k100p109

测试点 3T=5000n1018m1018k=0p109

测试点 4T=5000n1018m1018k=1p109

测试点 5T=5000n10m1018k10p109

测试点 6T=1n105m1018k105p109

测试点 7T=1n1018m1018k1018p109

测试点 8T=100n1018m1018k1018p109

测试点 910T=5000n1018m1018k1018p109

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

k 尝试拆开,如果 ij 小于等于 k 的那就不用看了,剩下的如果 ij 大于等于 k 的有 cnt 个,他们的和为 ans,那么答案就是 anscnt×k

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

#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 @   灰鲭鲨  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示