[SPOJ-QTREE6]Query on a tree VI 题解
题目地址:洛谷:【SP16549】QTREE6 – Query on a tr …
May all the beauty be blessed.
题目地址:洛谷:【SP2666】QTREE4 – Query on a tree IV – 洛谷、SPOJ:SPOJ.com – Problem QTREE4
SPOJ QTREE系列:
You are given a tree (an acyclic undirected connected graph) with N nodes, and nodes numbered 1,2,3…,N. Each edge has an integer value assigned to it(note that the value can be negative). Each node has a color, white or black. We define dist(a, b) as the sum of the value of the edges on the path from node a to node b.
All the nodes are white initially.
We will ask you to perfrom some instructions of the following form:
给一棵有边权的树,最初点全是白色的,操作:1.改变一个点的颜色(黑→白,白→黑)2.询问最远两个白点间距离。
输入格式:
In the first line there is an integer N (N <= 100000)
In the next N-1 lines, the i-th line describes the i-th edge: a line with three integers a b c denotes an edge between a, b of value c (-1000 <= c <= 1000)
In the next line, there is an integer Q denotes the number of instructions (Q <= 100000)
In the next Q lines, each line contains an instruction “C a” or “A”
输出格式:
For each “A” operation, write one integer representing its result. If there is no white node in the tree, you should write “They have disappeared.”.
输入样例#1:
3 1 2 1 1 3 1 7 A C 1 A C 2 A C 3 A
输出样例#1:
2 2 0 They have disappeared.
本题可以用点分治的做法解决,做法类似[ZJOI2007]捉迷藏 题解 | KSkun’s Blog。这里介绍边分治的做法。
我们在中心边位置维护两个堆,一个表示左边子树的各个白点距离,一个表示右边子树的。单独维护每个分治结构的答案,我们可以在一个统计最大值的时候顺带把子分治结构的最大值也计算进来,这样询问的时候只需要询问根分支结构的答案即可。在加点的的过程中,记录下这个点会影响到的堆的数据。变白要把这个点放进堆里,变黑只需要打标记。而每一次更新答案的时候,从堆顶把黑点全扔掉就好。如果用数组/vector来存的话,这个更新要根据倒序,因此倒序才是分治结构从底向根的顺序。
// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <queue>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
register char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
inline bool isop(char c) {
return c == 'A' || c == 'C';
}
inline char readop() {
char c;
while(!isop(c = fgc()));
return c;
}
const int MAXN = 200005, INF = 2e9;
int n, q, col[MAXN], ans;
struct Edge {
int to, w, nxt;
} gra[MAXN << 1], grao[MAXN << 1];
int head[MAXN], heado[MAXN], ecnt, ecnto;
inline void addedge(int u, int v, int w) {
gra[ecnt] = Edge {v, w, head[u]}; head[u] = ecnt++;
}
inline void addedgeo(int u, int v, int w) {
grao[ecnto] = Edge {v, w, heado[u]}; heado[u] = ecnto++;
}
inline void rebuild(int u, int fa) {
int ff = 0;
for(int i = heado[u]; ~i; i = grao[i].nxt) {
int v = grao[i].to, w = grao[i].w;
if(v == fa) continue;
if(!ff) {
addedge(u, v, w);
addedge(v, u, w);
ff = u;
} else {
int k = ++n;
col[k] = 1;
addedge(ff, k, 0);
addedge(k, ff, 0);
addedge(k, v, w);
addedge(v, k, w);
ff = k;
}
rebuild(v, u);
}
}
bool del[MAXN << 1];
int ct, ctsiz, sum;
int siz[MAXN], msz[MAXN];
inline void calsiz(int u, int fa) {
siz[u] = 1;
for(int i = head[u]; ~i; i = gra[i].nxt) {
int v = gra[i].to;
if(del[i >> 1] || v == fa) continue;
calsiz(v, u);
siz[u] += siz[v];
}
}
inline void findct(int u, int fa) {
for(int i = head[u]; ~i; i = gra[i].nxt) {
int v = gra[i].to;
if(del[i >> 1] || v == fa) continue;
findct(v, u);
int vsiz = std::max(siz[v], sum - siz[v]);
if(vsiz < ctsiz) {
ct = i;
ctsiz = vsiz;
}
}
}
struct DisData {
int u, d;
inline bool operator<(const DisData &rhs) const {
return d < rhs.d;
}
};
std::priority_queue<DisData> s[MAXN][2];
int cnt;
struct NodeData {
int bel, side, dis;
};
std::vector<NodeData> ndata[MAXN];
inline void caldis(int u, int fa, int d, int t, int l) {
if(!col[u]) {
s[t][l].push(DisData {u, d}); ndata[u].push_back(NodeData {t, l, d});
}
for(int i = head[u]; ~i; i = gra[i].nxt) {
int v = gra[i].to, w = gra[i].w;
if(del[i >> 1] || v == fa) continue;
caldis(v, u, d + w, t, l);
}
}
int mx[MAXN], lch[MAXN], rch[MAXN], ctw[MAXN];
inline void update(int p) {
while(!s[p][0].empty() && col[s[p][0].top().u]) s[p][0].pop();
while(!s[p][1].empty() && col[s[p][1].top().u]) s[p][1].pop();
if(s[p][0].empty() || s[p][1].empty()) mx[p] = 0;
else mx[p] = s[p][0].top().d + ctw[p] + s[p][1].top().d;
if(lch[p]) mx[p] = std::max(mx[p], mx[lch[p]]);
if(rch[p]) mx[p] = std::max(mx[p], mx[rch[p]]);
}
inline int divide(int u) {
calsiz(u, 0);
ct = -1; ctsiz = INF; sum = siz[u]; findct(u, 0);
if(ct == -1) return 0;
int x = gra[ct].to, y = gra[ct ^ 1].to;
del[ct >> 1] = true;
int t = ++cnt;
ctw[t] = gra[ct].w;
caldis(x, 0, 0, t, 0); caldis(y, 0, 0, t, 1);
lch[t] = divide(x); rch[t] = divide(y);
update(t);
return t;
}
inline void setwhite(int u) {
for(int i = ndata[u].size() - 1; i >= 0; i--) {
NodeData d = ndata[u][i];
s[d.bel][d.side].push(DisData {u, d.dis});
update(d.bel);
}
}
inline void setblack(int u) {
for(int i = ndata[u].size() - 1; i >= 0; i--) {
NodeData d = ndata[u][i];
update(d.bel);
}
}
int ut, vt, wt;
char op;
int main() {
memset(head, -1, sizeof(head));
memset(heado, -1, sizeof(heado));
n = readint();
int white = n;
for(int i = 1; i < n; i++) {
ut = readint(); vt = readint(); wt = readint();
addedgeo(ut, vt, wt);
addedgeo(vt, ut, wt);
}
rebuild(1, 0);
divide(1);
q = readint();
while(q--) {
op = readop();
if(op == 'A') {
if(!white) {
puts("They have disappeared.");
} else if(white == 1) {
puts("0");
} else {
printf("%d\n", mx[1]);
}
} else {
ut = readint();
col[ut] ^= 1;
if(col[ut]) {
setblack(ut);
white--;
} else {
setwhite(ut);
white++;
}
}
}
return 0;
}
题目地址:洛谷:【SP913】QTREE2 – Query on a tree II – 洛谷、SPOJ:SPOJ.com – Problem QTREE2
SPOJ QTREE系列:
You are given a tree (an undirected acyclic connected graph) with N nodes, and edges numbered 1, 2, 3…N-1. Each edge has an integer value assigned to it, representing its length.
We will ask you to perfrom some instructions of the following form:
给一棵带边权的树,操作1.询问两点路径长2.求两点有向路径上第k点。
输入格式:
The first line of input contains an integer t, the number of test cases (t <= 25). t test cases follow.
For each test case:
There is one blank line between successive tests.
输出格式:
For each “DIST” or “KTH” operation, write one integer representing its result.
Print one blank line after each test.
输入样例#1:
1 6 1 2 1 2 4 1 2 5 2 1 3 1 3 6 2 DIST 4 6 KTH 4 6 4 DONE
输出样例#1:
5 3
求和同QTREE:[SPOJ-QTREE]Query on a tree 题解 | KSkun’s Blog。查k点可以考虑算一下LCA到两个儿子的距离,看看这个点在哪条链上,然后再换算成底端往上第几个点,沿重链上跳,利用DFS序算出来即可。
// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline int readint() {
register int res = 0, neg = 1;
register char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
inline bool isop(char c) {
return c == 'I' || c == 'H' || c == 'O';
}
inline char readop() {
register char c;
while(!isop(c = fgc()));
return c;
}
const int MAXN = 10005;
struct Edge {
int to, w, nxt;
} gra[MAXN << 1];
int head[MAXN], tot;
int T, n, m, ut, vt, wt, kt;
char op;
int w[MAXN], fa[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], ptn[MAXN], top[MAXN], dep[MAXN], cnt;
inline void dfs1(int u) {
siz[u] = 1;
son[u] = 0;
for(register int i = head[u]; i; i = gra[i].nxt) {
register int v = gra[i].to;
if(v == fa[u]) continue;
dep[v] = dep[u] + 1;
fa[v] = u;
w[v] = gra[i].w;
dfs1(v);
siz[u] += siz[v];
if(siz[v] > siz[son[u]]) son[u] = v;
}
}
inline void dfs2(int u, int tp) {
top[u] = tp;
dfn[u] = ++cnt;
ptn[dfn[u]] = u;
if(son[u]) dfs2(son[u], tp);
for(register int i = head[u]; i; i = gra[i].nxt) {
register int v = gra[i].to;
if(v == son[u] || v == fa[u]) continue;
dfs2(v, v);
}
}
LL sgt[MAXN << 2];
inline void build(int o, int l, int r) {
if(l == r) {
sgt[o] = w[ptn[l]];
return;
}
register int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
build(lch, l, mid);
build(rch, mid + 1, r);
sgt[o] = sgt[lch] + sgt[rch];
}
inline void modify(int o, int l, int r, int x, int v) {
if(l == r) {
sgt[o] = v;
return;
}
register int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(x <= mid) modify(lch, l, mid, x, v);
else modify(rch, mid + 1, r, x, v);
sgt[o] = sgt[lch] + sgt[rch];
}
inline LL query(int o, int l, int r, int ll, int rr) {
if(l >= ll && r <= rr) {
return sgt[o];
}
register int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
register LL res = 0;
if(ll <= mid) res += query(lch, l, mid, ll, rr);
if(rr > mid) res += query(rch, mid + 1, r, ll, rr);
return res;
}
inline LL querysum(int u, int v) {
int tu = top[u], tv = top[v];
register LL res = 0;
while(tu != tv) {
if(dep[tu] > dep[tv]) {
std::swap(u, v);
std::swap(tu, tv);
}
res += query(1, 1, n, dfn[tv], dfn[v]);
v = fa[tv];
tv = top[v];
}
if(dep[u] > dep[v]) std::swap(u, v);
if(u != v) res += query(1, 1, n, dfn[u] + 1, dfn[v]);
return res;
}
inline int querylca(int u, int v) {
int tu = top[u], tv = top[v];
while(tu != tv) {
if(dep[tu] > dep[tv]) {
std::swap(u, v);
std::swap(tu, tv);
}
v = fa[tv];
tv = top[v];
}
if(dep[u] > dep[v]) std::swap(u, v);
return u;
}
inline int querykth(int u, int v, int k) {
int lca = querylca(u, v), tu = top[u], tv = top[v];
if(dep[u] - dep[lca] + 1 >= k) {
while(dep[tu] > dep[lca]) {
if(dep[u] - dep[tu] + 1 >= k) break;
k -= dep[u] - dep[tu] + 1;
u = fa[tu];
tu = top[u];
}
return ptn[dfn[u] - k + 1];
} else {
k -= dep[u] - dep[lca] + 1;
k = dep[v] - dep[lca] - k + 1;
while(dep[tv] > dep[lca]) {
if(dep[v] - dep[tv] + 1 >= k) break;
k -= dep[v] - dep[tv] + 1;
v = fa[tv];
tv = top[v];
}
return ptn[dfn[v] - k + 1];
}
}
inline void addedge(int u, int v, int w) {
gra[++tot] = Edge {v, w, head[u]};
head[u] = tot;
}
int main() {
T = readint();
while(T--) {
tot = cnt = 0;
memset(head, 0, sizeof(head));
n = readint();
for(int i = 1; i < n; i++) {
ut = readint(); vt = readint(); wt = readint();
addedge(ut, vt, wt);
addedge(vt, ut, wt);
}
dfs1(1);
dfs2(1, 1);
build(1, 1, n);
for(;;) {
op = readop();
if(op == 'O') break;
ut = readint();
vt = readint();
if(op == 'I') {
printf("%lld\n", querysum(ut, vt));
} else {
kt = readint();
printf("%d\n", querykth(ut, vt, kt));
}
}
printf("\n");
}
return 0;
}
题目地址:UOJ:共价大爷游长沙 – 题目 – Universal Online Judge
火车司机出秦川,跳蚤国王下江南,共价大爷游长沙。每个周末,勤劳的共价大爷都会开车游历长沙市。
长沙市的交通线路可以抽象成为一个 n 个点 n-1 条边的无向图,点编号为 1 到 n,任意两点间均存在恰好一条路径,显然两个点之间最多也只会有一条边相连。有一个包含一些点对 (x,y) 的可重集合 S,共价大爷的旅行路线是这样确定的:每次他会选择 S 中的某一对点 (x,y),并从 x 出发沿着唯一路径到达 y。
小L是共价大爷的脑残粉,为了见到共价大爷的尊容,小L决定守在这张图的某条边上等待共价大爷的到来。为了保证一定能见到他,显然小L必须选择共价大爷一定会经过的边——也就是所有共价大爷可能选择的路径都经过的边。
现在小L想知道,如果他守在某一条边,是否一定能见到共价大爷。
然而长沙市总是不断的施工,也就是说,可能某个时刻某条边会断开,同时这个时刻一定也有某条新边会出现,且任意时刻图都满足任意两点间均存在恰好一条路径的条件。注意断开的边有可能和加入的新边连接着相同的两个端点。共价大爷的兴趣也会不断变化,所以S也会不断加入新点对或者删除原有的点对。当然,小L也有可能在任何时候向你提出守在某一条边是否一定能见到共价大爷的问题。你能回答小L的所有问题吗?
输入格式:
输入的第一行包含一个整数 id,表示测试数据编号,如第一组数据的id=1,样例数据的 id 可以忽略。hack数据中的 id 必须为 0 到 10 之间的整数。hack数据中id的值和数据类型没有任何关系。
输入的第二行包含两个整数 n,m,分别表示图中的点数,以及接下来会发生的事件数,事件的定义下文中会有描述。初始时 S 为空。
接下来 n-1 行,每行两个正整数 x,y,表示点 x 和点 y 之间有一条无向边。
接下来 m 行,每行描述一个事件,每行的第一个数 type 表示事件的类型。
若type=1,那么接下来有四个正整数x,y,u,v,表示先删除连接点x和点y的无向边,保证存在这样的无向边,然后加入一条连接点u和点v的无向边,保证操作后的图仍然满足题中所述条件。
若type=2,那么接下来有两个正整数 x,y,表示在 S 中加入点对 (x,y)。
若type=3,那么接下来有一个正整数 x,表示删除第 x 个加入 S 中的点对,即在第 x 个 type=2 的事件中加入 S 中的点对,保证这个点对存在且仍然在 S 中。
若type=4,那么接下来有两个正整数 x,y,表示小L询问守在连接点 x 和点 y 的边上是否一定能见到共价大爷,保证存在这样的无向边且此时 S 不为空。
输出格式:
对于每个小L的询问,输出“YES”或者“NO”(均不含引号)表示小L一定能或者不一定能见到共价大爷。
输入样例#1:
0 5 7 1 2 1 3 2 4 1 5 2 1 5 1 1 5 2 5 4 2 5 2 1 4 4 2 5 3 1 4 2 4
输出样例#1:
YES NO YES
参考资料:【uoj207】 共价大爷游长沙 – MashiroSky – 博客园,感谢原作者。
这里我们使用异或标记链信息,使用异或的好处是便于处理子树信息,且操作可逆。
我们考虑如果一条边满足要求,那么所有的链端点肯定分布于这条边端点的两个子树中。既然如此,我们可以在链端点设置随机权值,并且用LCT来维护树的形态与子树的权值异或和。每次查询查这条边任意一端的子树异或和是否等于所有链权值的异或和。
注意LCT中子树信息不仅要维护Splay上的子树信息,还要维护轻边所连接的儿子信息,因此涉及到轻边重边变化的操作,如access、link、cut等,需要维护好轻儿子信息。维护轻儿子信息需要对LCT各种操作对树形态的影响十分熟悉,存在一定难度。代码中,我用val来表示该节点与所有轻儿子子树的异或和,sum表示所在Splay子树的val值异或和。查询时,Splay中只有u、v两个点,因此v的轻儿子子树加上v就是v所在子树,查val即可。
// Code by KSkun, 2018/3
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <algorithm>
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline int readint() {
register int res = 0, neg = 1;
char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 300005, INF = 1e9;
struct LCTNode {
int ch[2], fa, val, sum;
bool rev;
} lct[MAXN];
inline bool isleft(int p) {
return lct[lct[p].fa].ch[0] == p;
}
inline bool isroot(int p) {
register int fa = lct[p].fa;
return lct[fa].ch[0] != p && lct[fa].ch[1] != p;
}
inline void update(int p) {
register int ls = lct[p].ch[0], rs = lct[p].ch[1];
lct[p].sum = lct[p].val ^ lct[ls].sum ^ lct[rs].sum;
}
inline void reverse(int p) {
std::swap(lct[p].ch[0], lct[p].ch[1]);
lct[p].rev ^= 1;
}
inline void pushdown(int p) {
register int ls = lct[p].ch[0], rs = lct[p].ch[1];
if(lct[p].rev) {
if(ls) reverse(ls);
if(rs) reverse(rs);
lct[p].rev ^= 1;
}
}
int sta[MAXN], stop;
inline void pushto(int p) {
stop = 0;
while(!isroot(p)) {
sta[stop++] = p;
p = lct[p].fa;
}
pushdown(p);
while(stop) {
pushdown(sta[--stop]);
}
}
inline void rotate(int p) {
register bool t = !isleft(p); register int fa = lct[p].fa, ffa = lct[fa].fa;
lct[p].fa = ffa; if(!isroot(fa)) lct[ffa].ch[!isleft(fa)] = p;
lct[fa].ch[t] = lct[p].ch[!t]; lct[lct[fa].ch[t]].fa = fa;
lct[p].ch[!t] = fa; lct[fa].fa = p;
update(fa);
}
inline void splay(int p) {
pushto(p);
for(register int fa = lct[p].fa; !isroot(p); rotate(p), fa = lct[p].fa) {
if(!isroot(fa)) rotate(isleft(fa) == isleft(p) ? fa : p);
}
update(p);
}
inline void access(int p) {
for(register int q = 0; p; q = p, p = lct[p].fa) {
splay(p);
lct[p].val ^= lct[lct[p].ch[1]].sum;
lct[p].ch[1] = q;
lct[p].val ^= lct[lct[p].ch[1]].sum;
update(p);
}
}
inline void makert(int p) {
access(p);
splay(p);
reverse(p);
}
inline int findrt(int p) {
access(p);
splay(p);
while(lct[p].ch[0]) p = lct[p].ch[0];
return p;
}
inline void split(int u, int v) {
makert(u);
access(v);
splay(v);
}
inline void link(int u, int v) {
split(u, v);
lct[u].fa = v;
lct[v].val ^= lct[u].sum;
update(v);
}
inline void cut(int u, int v) {
split(u, v);
if(lct[v].ch[0] != u || lct[lct[v].ch[0]].ch[1]) return;
lct[u].fa = lct[v].ch[0] = 0;
update(v);
}
inline int query(int u, int v) {
makert(u);
access(v);
return lct[v].val;
}
inline void modify(int u, int w) {
access(u);
splay(u);
lct[u].val ^= w;
update(u);
}
int n, m, op, x, y, w, u, v, all;
struct Edge {
int u, v, w;
} edge[MAXN];
int tot;
int main() {
srand(time(NULL));
readint(); n = readint(); m = readint();
for(int i = 1; i < n; i++) {
u = readint(); v = readint();
link(u, v);
}
while(m--) {
op = readint();
switch(op) {
case 1:
x = readint(); y = readint(); u = readint(); v = readint();
cut(x, y);
link(u, v);
break;
case 2:
x = readint(); y = readint(); w = rand();
edge[++tot] = Edge {x, y, w};
modify(x, w);
modify(y, w);
all ^= w;
break;
case 3:
x = readint();
modify(edge[x].u, edge[x].w);
modify(edge[x].v, edge[x].w);
all ^= edge[x].w;
break;
case 4:
x = readint(); y = readint();
puts(query(x, y) == all ? "YES" : "NO");
}
}
return 0;
}