Luogu P4067 [SDOI2016]储能表

题目描述

https://www.luogu.com.cn/problem/P4067

Solution

我们考虑数位dp,令f[pos][0/1][0/1][0/1]表示有没有压nm的上界以及有没有压k的下界

在统计的时候一并维护方案数和sum

时间复杂度为 $O(Tlen2^{5})$

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 70
#define ll long long
using namespace std;

ll n, m, k; int p;

struct node {
int s, v;

node() {}
node(int _v, int _s) { v = _v; s = _s; }
} f[maxn][2][2];

int d1[maxn], d2[maxn], d3[maxn];
node dfs(int pos, bool lim1, bool lim2, bool lim3) {
if (!pos) return node(0, 1);
if (!lim3 && (~f[pos][lim1][lim2].v || ~f[pos][lim1][lim2].s)) return f[pos][lim1][lim2];
int up1 = lim1 ? d1[pos] : 1, up2 = lim2 ? d2[pos] : 1; node ans = node(0, 0);
for (int i = 0; i <= up1; ++i)
for (int j = 0; j <= up2; ++j) {
if (lim3 && (i ^ j) < d3[pos]) continue;
node t = dfs(pos - 1, lim1 && i == d1[pos], lim2 && j == d2[pos], lim3 && (i ^ j) == d3[pos]);
ans.s = (ans.s + t.s) % p;
ans.v = (ans.v + t.v + (i ^ j) * (1ll << pos - 1) % p * t.s) % p;
}
if (!lim3) f[pos][lim1][lim2] = ans;
return ans;
}

node solve(ll x1, ll x2, ll x3) {
int l1 = 0, l2 = 0, l3 = 0;
memset(d1, 0, sizeof d1); memset(d2, 0, sizeof d2); memset(d3, 0, sizeof d3);
while (x1) {
d1[++l1] = x1 % 2;
x1 /= 2;
}
while (x2) {
d2[++l2] = x2 % 2;
x2 /= 2;
}
while (x3) {
d3[++l3] = x3 % 2;
x3 /= 2;
}
int l = max(l1, max(l2, l3));
return dfs(l, 1, 1, 1);
}

void mem() {
for (int i = 0; i < maxn; ++i)
for (int j = 0; j < 2; ++j)
for (int k = 0; k < 2; ++k) f[i][j][k] = node(-1, -1);
}

void work() {
cin >> n >> m >> k >> p; --n; --m;
mem(); node t = solve(n, m, k);
cout << (t.v - t.s * (k % p) % p + p) % p << endl;
}

int main() {
int T; cin >> T;
while (T--) work();
return 0;
}