洛谷 P3203 [HNOI2010]弹飞绵羊 题解
一、题目:
二、思路:
好消息:我改用Atom编辑器了。Atom用的真舒服。
坏消息:最近脑子有点不好使,这么简单的题目想复杂了。
言归正传。
其实看到这种每个点只有一个依赖关系的时候,就要想一想基环树或者是树。他俩的区别就在于有没有一个“根”存在。
那么本题是有一个根的,这个根就是“被弹飞”。
具体来说就是:(我们采用编号\(1\sim n\),和题目有所不同)
- 若\(x+k\leq n\),则连一条边\((x,x+k)\)。
- 若\(x+k>n\),则连一条边\((x,n+1)\)。
其中\(n+1\)即为虚拟根,表示绵羊被弹飞。
那么题目中的修改操作就对应着树的删边和加边,所以要使用动态树这个数据结构。
查询操作即为从\(x\)到\(n+1\)的路径上点的个数,再减一。
做完了。
三、我坏掉的脑子
我原本没想到建一个大的虚拟根,而是森林中的每棵树都建了一个虚拟根,这样,如果删除一个节点和它的对应根之间的边,再连接到一个新树上去的话,还必须修改它所在树的虚拟根。极其麻烦。
四:代码:
正常代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int maxn = 4e5 + 5;
int n, fa[maxn], son[maxn][2], siz[maxn];
int K[maxn];
bool tag[maxn];
inline bool get(int x) {
return son[fa[x]][1] == x;
}
inline bool isroot(int x) {
return son[fa[x]][0] != x && son[fa[x]][1] != x;
}
inline void pushup(int x) {
siz[x] = siz[son[x][0]] + siz[son[x][1]] + 1;
}
inline void rev(int x) {
tag[x] ^= 1;
swap(son[x][0], son[x][1]);
}
inline void pushdown(int x) {
if (tag[x]) {
if (son[x][0]) rev(son[x][0]);
if (son[x][1]) rev(son[x][1]);
tag[x] = 0;
}
}
inline void rotate(int x) {
int y = fa[x], z = fa[y], k = get(x);
if (z && !isroot(y)) son[z][get(y)] = x;
son[y][k] = son[x][k ^ 1]; fa[son[y][k]] = y; fa[y] = x;
son[x][k ^ 1] = y; fa[x] = z;
pushup(y); pushup(x);
}
inline void correct(int x) {
if (!isroot(x)) correct(fa[x]);
pushdown(x);
}
inline void splay(int x) {
correct(x);
for (int f = fa[x]; !isroot(x); rotate(x), f = fa[x])
if (!isroot(f))
rotate(get(x) == get(f) ? f : x);
}
inline void access(int x) {
int z = x;
for (int y = 0; x; y = x, x = fa[x]) {
splay(x);
son[x][1] = y;
pushup(x);
}
splay(z);
}
inline int findroot(int x) {
access(x);
while (son[x][0])
pushdown(x), x = son[x][0];
splay(x);
return x;
}
inline void makeroot(int x) {
access(x);
rev(x);
}
inline void split(int x, int y) {
makeroot(x);
access(y);
}
inline void link(int x, int y) {
makeroot(x);
if (findroot(y) != x) {
fa[x] = y;
}
}
inline void cut(int x, int y) {
split(x, y);
if (son[y][0] == x && !son[x][1]) {
son[y][0] = 0; fa[x] = 0;
pushup(y);
}
}
int main() {
n = read();
for (int i = 1; i <= n; ++ i) {
K[i] = read();
if (i + K[i] > n) {
link(i, n + 1);
}
else link(i, i + K[i]);
}
int m = read();
while (m --) {
int opt = read();
if (opt == 1) {
int x = read();
++ x;
split(x, n + 1);
printf("%d\n", siz[n + 1] - 1);
} else {
int x = read(), upd = read();
++ x;
if (x + K[x] > n) {
cut(x, n + 1);
}
else cut(x, x + K[x]);
K[x] = upd;
if (x + K[x] > n) {
link(x, n + 1);
}
else link(x, x + K[x]);
}
}
return 0;
}
坏掉脑子的写法(已AC)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int maxn = 4e5 + 5;
int n, fa[maxn], son[maxn][2], siz[maxn], root[maxn];
int K[maxn];
bool tag[maxn];
inline bool get(int x) {
return son[fa[x]][1] == x;
}
inline bool isroot(int x) {
return son[fa[x]][0] != x && son[fa[x]][1] != x;
}
inline void pushup(int x) {
siz[x] = siz[son[x][0]] + siz[son[x][1]] + 1;
}
inline void rev(int x) {
tag[x] ^= 1;
swap(son[x][0], son[x][1]);
}
inline void pushdown(int x) {
if (tag[x]) {
if (son[x][0]) rev(son[x][0]);
if (son[x][1]) rev(son[x][1]);
tag[x] = 0;
}
}
inline void rotate(int x) {
int y = fa[x], z = fa[y], k = get(x);
if (z && !isroot(y)) son[z][get(y)] = x;
son[y][k] = son[x][k ^ 1]; fa[son[y][k]] = y; fa[y] = x;
son[x][k ^ 1] = y; fa[x] = z;
pushup(y); pushup(x);
}
inline void correct(int x) {
if (!isroot(x)) correct(fa[x]);
pushdown(x);
}
inline void splay(int x) {
correct(x);
for (int f = fa[x]; !isroot(x); rotate(x), f = fa[x])
if (!isroot(f))
rotate(get(x) == get(f) ? f : x);
}
inline void access(int x) {
int z = x;
for (int y = 0; x; y = x, x = fa[x]) {
splay(x);
son[x][1] = y;
pushup(x);
}
splay(z);
}
inline int findroot(int x) {
access(x);
while (son[x][0])
pushdown(x), x = son[x][0];
splay(x);
return x;
}
inline void makeroot(int x) {
int tmp = root[findroot(x)];
access(x);
root[x] = tmp;
rev(x);
}
inline void split(int x, int y) {
makeroot(x);
access(y);
}
inline void link(int x, int y) {
makeroot(x);
if (findroot(y) != x) {
fa[x] = y;
}
}
inline void cut(int x, int y) {
int tmp = root[findroot(x)];
split(x, y);
if (son[y][0] == x && !son[x][1]) {
son[y][0] = 0; fa[x] = 0;
pushup(y);
root[findroot(y)] = tmp;
}
}
int main() {
n = read();
int r = n;
for (int i = 1; i <= n; ++ i) {
K[i] = read();
if (i + K[i] > n) {
link(i, ++ r);
root[findroot(i)] = r;
}
else link(i, i + K[i]);
}
int m = read();
while (m --) {
int opt = read();
if (opt == 1) {
int x = read();
++ x;
int tmp = root[findroot(x)];
split(x, tmp);
printf("%d\n", siz[tmp] - 1);
} else {
int x = read(), upd = read();
++ x;
if (x + K[x] > n) {
int tmp = root[findroot(x)];
cut(x, tmp);
}
else cut(x, x + K[x]);
K[x] = upd;
if (x + K[x] > n) {
link(x, ++ r);
root[findroot(x)] = r;
}
else link(x, x + K[x]);
}
}
return 0;
}