题意:有n个节点,每个节点有一个权值,现有以下三种操作:
1、bridge A B 如果节点A、B连通输出no;否则输出yes,并在A、B之间连一条边;
2、penguins A X 将节点A的权值修改为X;
3、excursion A B 如果节点A、B不连通输出impossible;否则输出节点A到B的路径上的权值和。
思路:动态树模板题,1操作是动态树的删边操作,2操作是简单的修改,不过得先把节点提伸至根节点,3操作可以先把A节点成为该点所在树的根节点,然后B节点与根节点相连,然后再splay维护一下权值和即可。
#include
#include
#include
const int maxn = 3 * 1e4 + 10;
using namespace std;
struct link_cut {
private:
struct Node {
int idx, isrev, tal;
int fa, ch[2], path_pre;
};
Node node[maxn];
int nodecnt, loc[maxn];
int data[maxn];
void update(int x) {
int &ls = node[x].ch[0];
int &rs = node[x].ch[1];
int &id = node[x].idx;
node[x].tal = node[ls].tal + node[rs].tal + data[id];
}
///使树的形态正常化
void nor(int p) {
if(node[p].isrev) {
node[p].isrev = 0;
int &l = node[p].ch[0];
int &r = node[p].ch[1];
node[l].isrev ^= 1;
node[r].isrev ^= 1;
swap(l, r);
}
update(p);
}
///旋转
void __rotate(int x, int d) {
int y = node[x].fa;
node[x].path_pre = node[y].path_pre;
node[y].path_pre = 0;
node[y].ch[d ^ 1] = node[x].ch[d];
if(node[x].ch[d]) node[node[x].ch[d]].fa = y;
node[x].fa = node[y].fa;
if(node[y].fa) {
if(y == node[node[y].fa].ch[0])
node[node[y].fa].ch[0] = x;
else node[node[y].fa].ch[1] = x;
}
node[y].fa = x; node[x].ch[d] = y;
update(y); update(x);
}
///提伸x为该Spaly的根节点
void splay(int x) {
while(node[x].fa) {
int fa = node[x].fa;
nor(node[x].fa);
if(node[fa].ch[0]) nor(node[fa].ch[0]);
if(node[fa].ch[1]) nor(node[fa].ch[1]);
if(x == node[fa].ch[0]) __rotate(x, 1);
else __rotate(x, 0);
}
}
///将节点v通过实路径连接至根节点
void access(int p) {
splay(p); nor(p);
int q = node[p].ch[1];
node[p].ch[1] = node[q].fa = 0; ///断裂
node[q].path_pre = p;
for(q = node[p].path_pre; q; q = node[p].path_pre) {
splay(q); nor(q);
int r = node[q].ch[1];
node[r].fa = node[p].path_pre = 0;
node[r].path_pre = node[p].fa = q;
node[q].ch[1] = p; p = q;
}
splay(p);
}
///找p的根节点
int find_root(int p) {
access(p); splay(p); nor(p);
while(node[p].ch[0]) p = node[p].ch[0];
splay(p);
return p;
}
///将p到根节点上的所有边取反
///即p成为根节点
void evert(int p) {
access(p); splay(p);
node[p].isrev ^= 1;
nor(p);
}
///将边(u, v)断开
void cut(int p, int q) {
evert(p); access(q);
splay(q); nor(q);
node[node[q].ch[0]].fa = 0;
node[q].ch[0] = 0;
nor(q);
}
///连接边(u, v)
void link(int p, int q) {
evert(p); splay(p);
nor(p); access(q);
splay(q); nor(q);
node[p].ch[0] = q;
node[q].fa = p;
nor(p);
}
///修改权值
void change(int p, int d, int pre) {
splay(p);
node[p].tal += d - pre;
}
///p节点到q节点上的路径权值和
int sum(int p, int q) {
evert(p); splay(p);
nor(p); access(q);
splay(q); nor(q);
return node[q].tal;
}
public:
void init() {
nodecnt = node[0].tal = 0;
memset(loc, 0, sizeof loc);
memset(data, 0, sizeof data);
}
///创建一颗只有一个节点的Splay
void make_tree(int id) {
int p = ++nodecnt;
loc[id] = p; node[p].path_pre = 0;
node[p].fa = node[p].ch[0] = node[p].ch[1] = 0;
node[p].idx = id;
}
///用loc数组是因为点标号和节点标号可能不一样
int getroot(int id) { return node[find_root(loc[id])].idx; }
void add(int x, int y) { link(loc[x], loc[y]); }
void destroy(int x, int y) { cut(loc[x], loc[y]); }
void Change(int x, int d) { change(loc[x], d, data[x]); data[x] = d; }
int getsum(int x, int y) { return sum(loc[x], loc[y]); }
} tree;
int n, w, q, x, y;
char s[20];
int main() {
while(scanf("%d", &n) != EOF) {
tree.init();
for(int i = 1; i <= n; i++)
tree.make_tree(i);
for(int i = 1; i <= n; i++) {
scanf("%d", &w);
tree.Change(i, w);
}
scanf("%d", &q);
while(q--) {
scanf("%s %d %d", s, &x, &y);
if(s[0] == 'p') tree.Change(x, y);
else if(s[0] == 'b') {
int nx = tree.getroot(x);
int ny = tree.getroot(y);
if(nx == ny) printf("no\n");
else {
printf("yes\n");
tree.add(x, y);
}
} else {
int nx = tree.getroot(x);
int ny = tree.getroot(y);
if(nx != ny) printf("impossible\n");
else printf("%d\n", tree.getsum(x, y));
}
}
}
return 0;
}