[SPOJ-QTREE2]Query on a tree II 题解
题目地址:洛谷:【SP913】QTREE2 – Query on a tree …
May all the beauty be blessed.
题目地址:CodeChef:Gangsters of Treeland | CodeChef
树之大陆是一个有N座城市的王国(城市从0开始标号)。城市之间有N-1条道路链接,使得两点之间恰好有一条道路(也就是说,形成一棵树形结构)。城市0是首都。
初始时,每个城市都被一个帮会所控制。村民在相邻的城市间移动,如果这两个城市不是隶属于同一个帮会的势力范围内,那么需要支付一个单位的代价。
每一年都会有新的帮会涌入首都,他们会扩张自己的势力范围。具体说来,他们会占据从首都到u路径上的所有城市(包括首都和u)。因为这个原因,来往于城市间的代价变得琢磨不定,这让村民们很苦恼。于是他们找你来帮忙。
给定一个城市u,定义f(u)为以u为根的子树中所有结点到根结点的代价的平均值。
输入格式:
第一行一个整数T表示数据组数。接下来有T组数据,每组数据的第一行有一个整数N表示城市的数目。接下来的N-1行,每行有两个用空格隔开的整数Ai、Bi表示一条连接这两点的边。接下来的一行一个整数Q,表示接下来有Q组询问,每个询问包含一个字符t和一个整数u。
如果t = ‘O’,表示一个新的帮会占据了从首都到u路径上的城市。
如果t = ‘q’,表示询问f(u)。
输出格式:
对每一组询问,输出一行表示对应的答案。
输入样例#1:
1 13 0 1 0 2 1 11 1 10 1 9 9 12 2 5 5 8 2 4 2 3 4 6 4 7 7 q 0 O 4 q 6 q 2 O 9 q 9 q 2
输出样例#1:
2.0000000000 1.0000000000 0.8571428571 0.5000000000 1.8571428571
1≤T≤15
1≤N≤10^5
1≤Q≤10^5
数据保证所有N的总和不超过2 * 10^5。
数据保证所有Q的总和不超过2 * 10^5。
我们想到,O操作其实很像LCT的access操作。我们是不是可以考虑用LCT的access来做这个题。
对于询问,我们可以考虑求和再除,求和又因为树的形态确定可以通过DFS序求子树和,可以用线段树处理。考虑一次染色对答案的影响。假如access的过程中遇到一个轻边变重边的转折点,那么这个原来重边连接的子树的答案会+1,而现在重边连接的子树答案会-1。在access的操作过程中维护这个即可。
DFS序和子树大小可以一个DFS解决,由于一开始没有相同颜色的点,我们可以直接在LCT上标记所有边都是轻边。
这次的代码写的有点乱qwq。
// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
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;
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 == 'O' || c == 'q';
}
inline char readop() {
register char c;
while(!isop(c = fgc()));
return c;
}
const int MAXN = 100005, INF = 1e9;
int T, n, q, ut, vt;
char op;
std::vector<int> gra[MAXN];
int dfn[MAXN], ptn[MAXN], siz[MAXN], dep[MAXN], tot;
bool vis[MAXN];
inline void graphinit() {
for(int i = 1; i <= n; i++) gra[i].clear();
tot = 0;
memset(vis, 0, sizeof(vis));
}
#define lch o << 1
#define rch (o << 1) | 1
#define mid ((l + r) >> 1)
struct SGTNode {
LL val, add;
} sgt[MAXN << 2];
inline void sgtinit(int o, int l, int r) {
sgt[o].add = 0;
if(l == r) {
sgt[o].val = dep[ptn[l]];
return;
}
sgtinit(lch, l, mid);
sgtinit(rch, mid + 1, r);
sgt[o].val = sgt[lch].val + sgt[rch].val;
}
inline void pushdown(int o, int l, int r) {
if(sgt[o].add) {
sgt[lch].add += sgt[o].add;
sgt[rch].add += sgt[o].add;
sgt[lch].val += sgt[o].add * (mid - l + 1);
sgt[rch].val += sgt[o].add * (r - mid);
sgt[o].add = 0;
}
}
inline void modify(int o, int l, int r, int ll, int rr, LL v) {
if(l >= ll && r <= rr) {
sgt[o].add += v;
sgt[o].val += v * (r - l + 1);
return;
}
pushdown(o, l, r);
if(ll <= mid) modify(lch, l, mid, ll, rr, v);
if(rr > mid) modify(rch, mid + 1, r, ll, rr, v);
sgt[o].val = sgt[lch].val + sgt[rch].val;
}
inline LL query(int o, int l, int r, int ll, int rr) {
if(l >= ll && r <= rr) {
return sgt[o].val;
}
pushdown(o, l, r);
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;
}
#undef lch
#undef rch
#undef mid
struct LCTNode {
int ch[2], fa;
} 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 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;
}
inline void splay(int 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);
}
}
inline int getsubrt(int p) {
while(lct[p].ch[0]) p = lct[p].ch[0];
return p;
}
inline void access(int p) {
for(register int q = 0; p; lct[p].ch[1] = q, q = p, p = lct[p].fa) {
splay(p);
if(lct[p].ch[1]) {
int ch = getsubrt(lct[p].ch[1]);
modify(1, 1, n, dfn[ch], dfn[ch] + siz[ch] - 1, 1);
}
if(q) {
int ch = getsubrt(q);
modify(1, 1, n, dfn[ch], dfn[ch] + siz[ch] - 1, -1);
}
}
}
inline void dfs(int u) {
vis[u] = true;
dfn[u] = ++tot;
ptn[tot] = u;
siz[u] = 1;
for(int v : gra[u]) {
if(vis[v]) continue;
dep[v] = dep[u] + 1;
lct[v].fa = u;
dfs(v);
siz[u] += siz[v];
}
}
inline void lctinit() {
memset(lct, 0, sizeof(lct));
}
int main() {
T = readint();
while(T--) {
n = readint();
graphinit();
lctinit();
for(int i = 1; i < n; i++) {
ut = readint() + 1; vt = readint() + 1;
gra[ut].push_back(vt);
gra[vt].push_back(ut);
}
dfs(1);
sgtinit(1, 1, n);
q = readint();
while(q--) {
op = readop();
ut = readint() + 1;
if(op == 'O') {
access(ut);
} else {
printf("%.8lf\n", query(1, 1, n, dfn[ut], dfn[ut] + siz[ut] - 1) / double(siz[ut]));
}
}
}
return 0;
}
题目地址:BZOJ:Problem 3589. — 动态树
小明在楼下种了一棵动态树, 该树每天会在某些节点上长出一些果子. 这棵树的根节点为1, 它有n个节点, n-1条边.
别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件
事件0:
这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子.
事件1:
小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次.
初始时, 每个节点上都没有果子.
输入格式:
第一行一个整数n(1<=n<=200,000), 即节点数.
接下来n-1行, 每行两个数字u, v. 表示果子u和果子v之间有一条直接的边. 节点从1开始编号.
在接下来一个整数nQ(1<=nQ<=200,000), 表示事件.
最后nQ行, 每行开头要么是0, 要么是1.
如果是0, 表示这个事件是事件0. 这行接下来的2个整数u, delta表示以u为根的子树中的每个节点长出了delta个果子.
如果是1, 表示这个事件是事件1. 这行接下来一个整数K(1<=K<=5), 表示这次询问涉及K个树枝. 接下来K对整数u_k, v_k, 每个树枝从节点u_k到节点v_k. 由于果子数可能非常多, 请输出这个数模2^31的结果.
输出格式:
对于每个事件1, 输出询问的果子数.
输入样例#1:
5 1 2 2 3 2 4 1 5 3 0 1 1 0 2 3 1 2 3 1 1 4
输出样例#1:
13
1 <= n <= 200,000, 1 <= nQ <= 200,000, K = 5.
生成每个树枝的过程是这样的:先在树中随机找一个节点, 然后在这个节点到根的路径上随机选一个节点, 这两个节点就作为树枝的两端.
又是树链信息,套树剖。考虑线段树维护区间和,然后使用打标记的形式来处理链并的问题。链上的点都打上标记,只有打标记的点的权值才会计入答案,这个线段树写是容易的。记得每次清空这次打过的标记。
至于那个取模,模数是2147483648,我们可以用int自然溢出以后对2147483647取与,这样就能把符号位搞掉,得到取模后的答案,减小常数。
吐槽:不要用memset处理很大的数组,会TLE。线段树做是O(\log n)比memsetO(n)优。
// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>
#include <vector>
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;
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 = 200005;
int n, q, ut, vt, op, xt, kt;
int fa[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], ptn[MAXN], top[MAXN], dep[MAXN], cnt;
std::vector<int> gra[MAXN];
inline void addedge(int u, int v) {
gra[u].push_back(v);
gra[v].push_back(u);
}
inline void dfs1(int u) {
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == fa[u]) continue;
dep[v] = dep[u] + 1;
fa[v] = u;
dfs1(v);
siz[u] += siz[v] + 1;
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(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == son[u] || v == fa[u]) continue;
dfs2(v, v);
}
}
int val[MAXN << 2], sum[MAXN << 2], add[MAXN << 2], tag[MAXN << 2];
inline void pushdown(int o, int l, int r) {
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(add[o]) {
add[lch] += add[o];
add[rch] += add[o];
val[lch] += add[o] * (mid - l + 1);
val[rch] += add[o] * (r - mid);
add[o] = 0;
}
if(tag[o] != -1) {
tag[lch] = tag[rch] = tag[o];
sum[lch] = val[lch] * tag[o];
sum[rch] = val[rch] * tag[o];
tag[o] = -1;
}
}
inline void modify(int o, int l, int r, int ll, int rr, int v) {
if(l >= ll && r <= rr) {
val[o] += v * (r - l + 1);
add[o] += v;
return;
}
pushdown(o, l, r);
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(ll <= mid) modify(lch, l, mid, ll, rr, v);
if(rr > mid) modify(rch, mid + 1, r, ll, rr, v);
val[o] = val[lch] + val[rch];
}
inline void select(int o, int l, int r, int ll, int rr, int v) {
if(l >= ll && r <= rr) {
sum[o] = val[o] * v;
tag[o] = v;
return;
}
pushdown(o, l, r);
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(ll <= mid) select(lch, l, mid, ll, rr, v);
if(rr > mid) select(rch, mid + 1, r, ll, rr, v);
sum[o] = sum[lch] + sum[rch];
}
inline void modify(int u, int v, int x) {
int tu = top[u], tv = top[v];
while(tu != tv) {
if(dep[tu] > dep[tv]) {
std::swap(u, v);
std::swap(tu, tv);
}
modify(1, 1, n, dfn[tv], dfn[v], x);
v = fa[tv];
tv = top[v];
}
if(dep[u] > dep[v]) std::swap(u, v);
modify(1, 1, n, dfn[u], dfn[v], x);
}
inline void select(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);
}
select(1, 1, n, dfn[tv], dfn[v], 1);
v = fa[tv];
tv = top[v];
}
if(dep[u] > dep[v]) std::swap(u, v);
select(1, 1, n, dfn[u], dfn[v], 1);
}
int main() {
n = readint();
for(int i = 1; i < n; i++) {
ut = readint(); vt = readint();
addedge(ut, vt);
}
dfs1(1);
dfs2(1, 1);
q = readint();
while(q--) {
op = readint();
if(op == 0) {
ut = readint(); xt = readint();
modify(1, 1, n, dfn[ut], dfn[ut] + siz[ut], xt);
} else {
kt = readint();
while(kt--) {
ut = readint(); vt = readint();
select(ut, vt);
}
printf("%d\n", sum[1] & 2147483647);
select(1, 1, n, 1, n, 0);
}
}
return 0;
}
题目地址:洛谷:【P3178】[HAOI2015]树上操作 – 洛谷、BZOJ:Problem 4034. — [HAOI2015]树上操作
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
输入格式:
第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) 。再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
输出格式:
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
输入样例#1:
5 5 1 2 3 4 5 1 2 1 4 2 3 2 5 3 3 1 2 1 3 5 2 1 2 3 3
输出样例#1:
6 9 13
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。
比树剖那篇文章的例题还裸。
// Code by KSkun, 2018/3
#include <cstdio>
#include <vector>
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;
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 = 100005;
int n, m, w[MAXN], ut, vt, op, xt, at;
int fa[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], ptn[MAXN], top[MAXN], dep[MAXN], cnt;
std::vector<int> gra[MAXN];
inline void addedge(int u, int v) {
gra[u].push_back(v);
gra[v].push_back(u);
}
inline void dfs1(int u) {
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == fa[u]) continue;
dep[v] = dep[u] + 1;
fa[v] = u;
dfs1(v);
siz[u] += siz[v] + 1;
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(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == son[u] || v == fa[u]) continue;
dfs2(v, v);
}
}
struct Node {
LL val, add;
} sgt[MAXN << 2];
inline void pushdown(int o, int l, int r) {
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(sgt[o].add) {
sgt[lch].add += sgt[o].add;
sgt[rch].add += sgt[o].add;
sgt[lch].val += sgt[o].add * (mid - l + 1);
sgt[rch].val += sgt[o].add * (r - mid);
sgt[o].add = 0;
}
}
inline void build(int o, int l, int r) {
if(l == r) {
sgt[o].val = w[ptn[l]];
return;
}
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
build(lch, l, mid);
build(rch, mid + 1, r);
sgt[o].val = sgt[lch].val + sgt[rch].val;
}
inline void add(int o, int l, int r, int ll, int rr, LL v) {
if(l >= ll && r <= rr) {
sgt[o].add += v;
sgt[o].val += v * (r - l + 1);
return;
}
pushdown(o, l, r);
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(ll <= mid) add(lch, l, mid, ll, rr, v);
if(rr > mid) add(rch, mid + 1, r, ll, rr, v);
sgt[o].val = sgt[lch].val + sgt[rch].val;
}
inline LL query(int o, int l, int r, int ll, int rr) {
if(l >= ll && r <= rr) {
return sgt[o].val;
}
pushdown(o, l, r);
LL res = 0;
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(ll <= mid) res += query(lch, l, mid, ll, rr);
if(rr > mid) res += query(rch, mid + 1, r, ll, rr);
return res;
}
inline void addpoint(int x, LL v) {
add(1, 1, n, dfn[x], dfn[x], v);
}
inline void addsubtree(int x, LL v) {
add(1, 1, n, dfn[x], dfn[x] + siz[x], v);
}
inline LL querychain(int x) {
int tp = top[x];
LL res = 0;
while(x) {
res += query(1, 1, n, dfn[tp], dfn[x]);
x = fa[tp];
tp = top[x];
}
return res;
}
int main() {
n = readint(); m = readint();
for(int i = 1; i <= n; i++) w[i] = readint();
for(int i = 1; i < n; i++) {
ut = readint(); vt = readint();
addedge(ut, vt);
}
dfs1(1);
dfs2(1, 1);
build(1, 1, n);
while(m--) {
op = readint();
xt = readint();
if(op != 3) at = readint();
if(op == 1) {
addpoint(xt, at);
}
if(op == 2) {
addsubtree(xt, at);
}
if(op == 3) {
printf("%lld\n", querychain(xt));
}
}
return 0;
}