贪心 + 枚举,枚举 + 按位考虑,期望dp + 树形dp + 换根,线段树

# A. 饥饿的小鸟(river)

对于一个位置 ii,枚举 iLi - Li1i - 1,显然从越早的荷叶跳过来越优,开个指针记录一下枚举到的位置即可。

复杂度 O(n)O(n)

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
#include <bits/stdc++.h>
#define pb push_back
#define int long long

using namespace std;

namespace IO{
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}

template <typename T> inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;

const int N = 1e5 + 10;
const int inf = 1e17;
int n, L;
int a[N], f[N];

signed main(){
// #ifndef ONLINE_JUDGE
// freopen("test.in", "r", stdin);
// freopen("test.out", "w", stdout);
// #endif
n = read(), L = read();
for(int i = 1; i < n; ++i) a[i] = read();
a[0] = inf, a[n] = inf;
f[0] = inf;
int p = 0;
for(int i = 1, j; i <= n; ++i){
if(i - L <= 0) {f[i] = a[i]; continue;}
for(j = max(i - L, p); j < i; ++j){
if(f[i] + f[j] <= a[i]) f[i] += f[j], f[j] = 0;
else {f[j] -= (a[i] - f[i]), f[i] = a[i]; break;}
}
p = j;
}
write(f[n]), puts("");
return 0;
}

# B. 进化序列(evolve)

非常水的题。

开个桶维护二进制下每一位出现次数,显然对于 ll 单调向右,最靠右合法的 rr 是单调递增的。

所以像莫队一样维护桶即可,复杂度 O(nlogV)O(n \log V)

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
#include <bits/stdc++.h>
#define pb push_back
#define int long long

using namespace std;

namespace IO{
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}

template <typename T> inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;

const int N = 1e5 + 10;
int n, m, ans, sum;
int a[N], buk[40];

inline void add(int x){
int pos = 0;
while(x){
if(x & 1){
if(!buk[pos]) sum += (1 << pos);
buk[pos]++;
}
x >>= 1, pos++;
}
}

inline void del(int x){
int pos = 0;
while(x){
if(x & 1){
buk[pos]--;
if(!buk[pos]) sum -= (1 << pos);
}
x >>= 1, pos++;
}
}

signed main(){
// #ifndef ONLINE_JUDGE
// freopen("test.in", "r", stdin);
// freopen("test.out", "w", stdout);
// #endif
double st = clock();
n = read(), m = read();
for(int i = 1; i <= n; ++i) a[i] = read();
int l = 1;
for(int i = 1; i <= n; ++i){
add(a[i]);
while(l < i && sum >= m) del(a[l++]);
ans += (i - l);
}
write(ans), puts("");
double ed = clock();
cerr << ed - st << endl;
return 0;
}

# C. 旅行 travel

首先 E(x2)E(x)×E(x)E(x^2) \neq E(x) \times E(x),因为事件不独立。

但是有 E(x+x)=E(x)+E(x)E(x + x) = E(x) + E(x)

所以推一下:E((a+b)2)=E(a2+b2+2ab)=E(a2)+E(b2)+E(2ab)E((a + b)^2) = E(a^2 + b^2 + 2ab) = E(a^2) + E(b^2) + E(2ab)

以下两个数组都是以 1 为根的情况下定义的。

我们设 hxh_x 表示 xx 子树内的点的 FF 和的期望,fxf_x 表示子树内 FyF_y 和的平方的期望。

显然有:

hx=(1p)×hx+p×(hx+hy)fx=(1p)×fx+p×(fx+fy+2hxhy)h_x = (1 - p) \times h_x + p \times (h_x + h_y) \\ f_x = (1 - p) \times f_x + p \times (f_x + f_y + 2h_xh_y)

(注意要先转移 ff,再转移 hh

gxg_x 为以 1 为根的情况下 xx 子树外(不包括 xx)的 FF 的和的期望,ansxans_x 为答案,即 xx 连通块的 FF 的和的平方的期望。

接下来看如何换根。

一个比较无脑的思路是维护 xx 儿子的前缀后缀和,然后各种转移,这里给一个代码简洁很多的做法(当然要推式子)。

考虑根从 uu 换到 vv

gv=(1p)×hv+p×(hv+E1)g_v = (1 - p) \times h_v + p \times (h_v + E_1)

E1E_1 为当前情况下 vv 以外的点 FF 的和的期望。那么 E1E_1 怎么求呢?

观察到对于 gug_u 我们有这样的式子:

gu=(1p)×E1+p×(hv+E1)g_u = (1 - p) \times E_1 + p \times (h_v + E_1)

而这个时候 gug_u 是已知的,所以直接计算出 E1E_1 再代回到刚才的式子中计算 gvg_v 即可。

ansans 数组也是同理的,对于 ansvans_v 有:

ansv=(1p)×fv+p×(fv+E2+2hvE1)ans_v = (1 - p) \times f_v + p \times (f_v + E_2 + 2h_vE_1)

这就相当于 E((a+b)2)=E(a2)+E(b2)+E(2ab)E((a + b)^2) = E(a^2) + E(b^2) + E(2ab)

同理,ansuans_u 有:

ansu=(1p)×E2+p×(fv+E2+2hvE1)ans_u = (1 - p) \times E_2 + p \times (f_v + E_2 + 2h_vE_1)

也是计算出 E2E_2 再代回去算 ansvans_v 即可。

代码还是有一定细节的,最重要的是不要忘记取模。

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <bits/stdc++.h>
#define pb push_back
#define db double
#define p(i) edge[i].w

using namespace std;

namespace IO{
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}

template <typename T> inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;

const int N = 2e5 + 10;
const int mod = 998244353;
int n, m;
int a[N], d[N];
struct node{
int v, w, nxt;
}edge[N << 1];
int head[N], tot;

inline int add(int x) {return x >= mod ? x - mod : x;}
inline int sub(int x) {return x < 0 ? x + mod : x;}
inline int mul(int x, int y) {return 1ll * x * y % mod;}

inline void add(int x, int y, int z){
edge[++tot] = (node){y, z, head[x]};
head[x] = tot;
}

int fa[N][20], dep[N];

inline void dfs(int x, int f){
fa[x][0] = f, dep[x] = dep[f] + 1;
for(int i = 1; i <= 18; ++i) fa[x][i] = fa[fa[x][i - 1]][i - 1];
for(int i = head[x], y; i; i = edge[i].nxt)
if((y = edge[i].v) != f) dfs(y, x);
}

inline int get_fa(int x, int k){
for(int i = 18; i >= 0; --i)
if(k >= (1 << i)) x = fa[x][i], k -= (1 << i);
return x;
}

int F[N], f1[N], f2[N], g[N], ans[N];

inline void calc_F(int x){
for(int i = head[x], y; i; i = edge[i].nxt){
if((y = edge[i].v) == fa[x][0]) continue;
calc_F(y), F[x] = add(F[x] + F[y]);
}
}

inline void calc_E(int x){
f1[x] = F[x], f2[x] = mul(F[x], F[x]);
for(int i = head[x], y; i; i = edge[i].nxt){
if((y = edge[i].v) == fa[x][0]) continue;
calc_E(y);
f2[x] = add(f2[x] + mul(p(i), add(f2[y] + 1ll * 2 * f1[x] * f1[y] % mod)));
f1[x] = add(f1[x] + mul(p(i), f1[y]));
}
}

inline void calc_ans(int x){
for(int i = head[x], y; i; i = edge[i].nxt){
if((y = edge[i].v) == fa[x][0]) continue;
int E1 = sub(g[x] - mul(p(i), f1[y])), K = mul(mul(2, E1), f1[y]);
g[y] = add(f1[y] + mul(p(i), E1));
int E2 = sub(ans[x] - mul(p(i), add(f2[y] + K)));
ans[y] = add(f2[y] + mul(p(i), add(E2 + K)));
calc_ans(y);
}
}

signed main(){
// #ifndef ONLINE_JUDGE
// freopen("test.in", "r", stdin);
// freopen("test.out", "w", stdout);
// #endif
n = read();
for(int i = 1; i <= n; ++i) a[i] = read(), d[i] = read();
for(int i = 1; i < n; ++i){
int x = read(), v = read(), w = read();
add(x, v, w), add(v, x, w);
}
dfs(1, 0), F[1] = a[1];
for(int i = 2; i <= n; ++i){
int p = get_fa(i, d[i] + 1);
F[i] += a[i], F[p] = sub(F[p] - a[i]);
}
calc_F(1), calc_E(1);
g[1] = f1[1], ans[1] = f2[1];
calc_ans(1);
m = read();
while(m--) write(ans[read()]), puts("");
return 0;
}

# D. 平衡树 splay

考场上一眼是个 LCT\text{LCT} 裸题,但是没想到 rotaterotate 之后中序遍历不变实在是降智了。

不难发现,旋转之后树的中序遍历不变。

所以我们对中序遍历维护一个线段树,每次旋转就相当于单点修改了两个数的值。

然后维护区间乘积即可。

代码也是十分的板子,rotaterotate 不要写挂了就好/kk

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
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <bits/stdc++.h>
#define pb push_back
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define int long long

using namespace std;

namespace IO{
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}

template <typename T> inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;

const int N = 2e5 + 10;
const int mod = 1e9 + 7;
int n, m;
int w[N], ch[N][2], sum[N << 2], val[N << 2], fa[N];
int in[N], out[N];

inline int add(int x) {return x >= mod ? x - mod : x;}
inline int mul(int x, int y) {return 1ll * x * y % mod;}

int dfn[N], tim, id[N];

inline void upd(int x){
sum[x] = add(w[x] + add(sum[ls(x)] + sum[rs(x)]));
in[x] = ls(x) ? in[ls(x)] : dfn[x];
out[x] = rs(x) ? out[rs(x)] : dfn[x];
}

inline void dfs(int x){
if(ls(x)) dfs(ls(x));
dfn[x] = ++tim, id[tim] = x;
if(rs(x)) dfs(rs(x));
upd(x);
}

#define lc rt << 1
#define rc rt << 1 | 1
#define mid ((l + r) >> 1)

inline void pushup(int rt){
val[rt] = mul(val[lc], val[rc]);
}

inline void build(int l, int r, int rt){
if(l == r) return val[rt] = sum[id[l]], void();
build(l, mid, lc), build(mid + 1, r, rc);
pushup(rt);
}

inline void update(int x, int k, int l, int r, int rt){
if(l == r) return val[rt] = k, void();
if(x <= mid) update(x, k, l, mid, lc);
else update(x, k, mid + 1, r, rc);
pushup(rt);
}

inline int query(int L, int R, int l, int r, int rt){
if(L > r || R < l) return 1;
if(L <= l && r <= R) return val[rt];
return mul(query(L, R, l, mid, lc), query(L, R, mid + 1, r, rc));
}

inline void rotate(int x){
if(!x) return;
int y = fa[x], z = fa[y];
int k = (rs(y) == x);
ch[z][(rs(z) == y)] = x;
fa[x] = z;
ch[y][k] = ch[x][k ^ 1];
fa[ch[x][k ^ 1]] = y;
ch[x][k ^ 1] = y, fa[y] = x;
upd(y), update(dfn[y], sum[y], 1, n, 1);
upd(x), update(dfn[x], sum[x], 1, n, 1);
}

signed main(){
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
n = read(), m = read();
for(int i = 1; i <= n; ++i){
w[i] = read(), ch[i][0] = read(), ch[i][1] = read();
fa[ls(i)] = fa[rs(i)] = i;
}
dfs(1), build(1, n, 1);
while(m--){
int op = read(), x = read();
if(op == 0) rotate(ls(x));
if(op == 1) rotate(rs(x));
if(op == 2) write(query(in[x], out[x], 1, n, 1)), puts("");
}
return 0;
}

更新于 阅读次数