[SDOI2016] 储能表
[SDOI2016]储能表
题目描述
有一个 n 行 m 列的表格,行从 0 到 n−1 编号,列从 0 到 m−1 编号。每个格子都储存着能量。最初,第 i 行第 j 列的格子储存着 (i xor j) 点能量。所以,整个表格储存的总能量是,
随着时间的推移,格子中的能量会渐渐减少。一个时间单位,每个格子中的能量都会减少 1。显然,一个格子的能量减少到 0 之后就不会再减少了。
也就是说,k 个时间单位后,整个表格储存的总能量是
给出一个表格,求 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;
}

浙公网安备 33010602011771号