0%

BZOJ 4513 [Sdoi2016]储能表

有一个 n 行 m 列的表格,行从 0 到 n−1 编号,列从 0 到 m−1 编号。每个格子都储存着能量。最初,第 i 行第 j 列的格子储存着 (i xor j) 点能量。所以,整个表格储存的总能量是,\[\sum\limits_{i = 0} ^ {n - 1} \sum\limits_{j = 0} ^ {m - 1} (i \ {\rm xor} \ j)\] 随着时间的推移,格子中的能量会渐渐减少。一个时间单位,每个格子中的能量都会减少 1。显然,一个格子的能量减少到 0 之后就不会再减少了。 也就是说,k 个时间单位后,整个表格储存的总能量是,\[\sum\limits_{i = 0} ^ {n - 1} \sum\limits_{j = 0} ^ {m - 1} \max((i \ {\rm xor} \ j) - k, 0)\] 给出一个表格,求 k 个时间单位后它储存的总能量。 由于总能量可能较大,输出时对 p 取模。

http://www.lydsy.com/JudgeOnline/problem.php?id=4513 好难的数位DP啊...感觉网上并没有太好的题解,理解了好长时间... 我主要参考的题解:http://fancypei.github.io/2016/04/16/SDOI2016%20Round1/#排列计数 其实是数位DP的传统套路,按位进行讨论,讨论的时候,没有上限(下限)的时候可以枚举所有的0和1,但是之前的所有位到达上限(下限)的时候,就不能枚举了,只能和之前的一样,或者限制的值为1,可以枚举0(限制的值为0,可以枚举1)。 那么我们的状态设计就是: \(f[x][0/1][0/1][0/1]\)表示从最高位枚举到了第\(x\)位的全部有贡献的状态的结果和,第二维代表枚举值和\(n\)的关系,第三维表示枚举值和\(m\)的关系,第四维表示枚举值和\(k\)的关系,值为0,就代表之前的位还没有到达限制,这一位可以随便枚举,值为1,代表之前的位已经到达限制了,这一位需要被原有值的限制。 \(g[x][0/1][0/1][0/1]\)表示从最高位枚举到了第\(x\)位的全部有贡献的状态数,状态设计和上一个数组同理。 然后枚举上一位的状态\(a,b,c\)和这一位的数值\(xx,yy,zz\),通过给定的这一位数值,来计算出这一位的状态\(aa,bb,cc\)。 对于方案数的状态转移非常简单,就是\[g[i][aa][bb][cc]+=g[i+1]_{abc}\] 对于结果的话,要计算出这一位对于结果的贡献,实际上,二进制的减法运算可以等效为列竖式的样子,各个位数的结果可以独立,到时候相加就可以了。 知道这个理论的话,方案的结果转移也比较简单\[f[i][aa][bb][cc]+=(zz-z)\times 2^i \times g[i+1]_{abc}\]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<bits/stdc++.h>
using namespace std;
long long T,n,m,k,mod,f[62][2][2][2],g[62][2][2][2];
int main()
{
scanf("%lld",&T);
while(T--)
{
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
scanf("%lld%lld%lld%lld",&n,&m,&k,&mod);
g[61][1][1][1]=1;
for(int i=60;i>=0;i--)
{
int x=(n>>i)&1,y=(m>>i)&1,z=(k>>i)&1;
for(int a=0;a<2;a++)
for(int b=0;b<2;b++)
for(int c=0;c<2;c++)
if(g[i+1][a][b][c])
{
for(int xx=0;xx<2;xx++)
for(int yy=0;yy<2;yy++)
{
int zz=xx^yy;
if((a&&x<xx)||(b&&y<yy)||(c&&z>zz)) continue;
int aa=(a&&x==xx),bb=(b&&y==yy),cc=(c&&z==zz);
g[i][aa][bb][cc]=(g[i][aa][bb][cc]+g[i+1][a][b][c])%mod;
f[i][aa][bb][cc]=(f[i][aa][bb][cc]+f[i+1][a][b][c]+((zz-z)+mod)%mod*((1ll<<i)%mod)%mod*g[i+1][a][b][c]%mod)%mod;
}
}
}
printf("%lld\n",f[0][0][0][0]);
}
}