抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

给你一棵 k (k30)k\ (k\le 30) 阶的满二叉树,从上到下、从左到右从 11 开始编号,设 {pn}\{p_{n}\} 是它的 Prüfer 序列。给你三个数 a,d,ma,d,m,求 i=0m1pa+id\sum_{i=0}^{m-1}p_{a+id}

这题主要是要发现一个性质,就是考虑定义 fi,k,h(x)f_{i,k,h}(x) 表示以 xx 为根,深度为 kk 的子树,现在要开始删的是 Prüfer 序列中的第 ii 位,对答案的共献,hh表示 xx 是否有父亲,那么 fi,k,h(x)f_{i,k,h}(x) 可以表示成 ai,k,hx+bi,k,hx2+ci,k,ha_{i,k,h}x+b_{i,k,h}\lfloor\frac{x}{2}\rfloor+c_{i,k,h} 的形式。

QQ 表示询问集合,考虑左儿子对答案的贡献为fi,k1,1(2x)f_{i,k-1,1}(2x),也就是对父亲的贡献是2ai,k1,1x+bi,k1,12x2+ci,k1,1=(2ai,k1,1+bi,k1,1)x+ci,k1,12a_{i,k-1,1}x+b_{i,k-1,1}\lfloor\frac{2x}{2}\rfloor+c_{i,k-1,1}=\left(2a_{i,k-1,1}+b_{i,k-1,1}\right)x+c_{i,k-1,1},即{2ai,k1,1+bi,k1,1,0,ci,k1,1}\{2a_{i,k-1,1}+b_{i,k-1,1},0,c_{i,k-1,1}\},右儿子同理可推出,为{2a+b,0,a+c}\{2a+b,0,a+c\}

我们考虑记搜,记搜的答案直接存的是 a,b,ca,b,c,这样我们就可以省掉xx 对答案的影响。我们发现 ii 其实可以表示成到下一个询问的距离,也就是其 lower_bound,这样我们可以省一点空间。

但是我们发现还是需要 2n2^n 级别,我们考虑小范围暴搜,其余记搜,即 k>15k>15 是不计入状态,然后如果递归到一棵子树发现它没有询问节点,那么直接 return 掉,这样就使得 ii 小于等于子树 size 了。

其实 h=0h=0 我们也不用计入状态,因为不难发现 h=0h=0 只会搜索 O(k)\mathcal{O}(k) 次,完全可以接受。

最后稍稍判一下边界即可。

这样深度小于等于 k2\frac{k}{2} 的节点只有 O(2k2)\mathcal{O}(2^\frac{k}{2}) 个,深度大于 k2\frac{k}{2} 的只有 O(k2k2)\mathcal{O}(k2^\frac{k}{2}) 个,所以单次询问时空复杂度均为O(k2k2)\mathcal{O}(k2^\frac{k}{2})

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int maxl = 16;
const int lim = 15;

struct Solution {
LL a, b, c;
Solution (LL a = 0, LL b = 0, LL c = 0) { this->a = a, this->b = b, this->c = c; }
inline LL calc(int x) { return a * x + b * (x >> 1) + c; }
} F[maxl][1 << maxl];

int vis[maxl][1 << maxl];

inline void addls(Solution& a, const Solution b) {
a.a += 2 * b.a + b.b;
a.c += b.c;
}

inline void addrs(Solution& a, const Solution b) {
a.a += 2 * b.a + b.b;
a.c += b.a + b.c;
}

int kase;

#define jian(x, y) { \
x -= y; \
}

inline Solution f(int k, int first, int m, int d, bool h) {
if (k == 1) {
return Solution(0, 1, 0);
}
int siz = (1 << k) - 1;
if (siz < first || !m) {
return Solution(0, 0, 0);
}
bool flg = k <= lim && h && first + m * d >= siz;
if (flg) {
if (vis[k][first] == kase) {
return F[k][first];
}
}
int fstbak = first;
Solution ans;
int sizs = (1 << (k - 1)) - 1;
Solution xx;
if (first < sizs && m > 0) {
int nowm = min(m, (sizs - first - 1) / d + 1);
addls(ans, xx = f(k - 1, first, nowm, d, true));
first += nowm * d;
m -= nowm;
}
jian(first, sizs);
if (!h) {
if (m > 0 && !first) {
ans.a += 2, ans.c++;
first += d, m--;
}
jian(first, 1);
}
if (first < sizs && m > 0) {
int nowm = min(m, (sizs - first - 1) / d + 1);
addrs(ans, xx = f(k - 1, first, nowm, d, h));
first += nowm * d;
m -= nowm;
}
jian(first, sizs);
if (h && m > 0) {
ans.b++;
}
if (flg) {
vis[k][fstbak] = kase;
F[k][fstbak] = ans;
}
return ans;
}

int main() {
int k, q;
cin >> k >> q;
while (q--) {
int a, d, m;
cin >> a >> d >> m;
a--, kase++;
Solution ans = f(k, a, m, d, false);
cout << ans.a + ans.c << '\n';
}
return 0;
}

评论