标签: 线段树

[SPOJ-QTREE2]Query on a tree II 题解

[SPOJ-QTREE2]Query on a tree II 题解

题目地址:洛谷:【SP913】QTREE2 – Query on a tree 

[SPOJ-QTREE]Query on a tree 题解

[SPOJ-QTREE]Query on a tree 题解

题目地址:洛谷:【SP375】QTREE – Query on a tree  

[CC-MONOPLOY]Gangsters of Treeland 题解

[CC-MONOPLOY]Gangsters of Treeland 题解

题目地址: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;
}
[CC-GERALD07]Chef and Graph Queries 题解

[CC-GERALD07]Chef and Graph Queries 题解

题目地址:CodeChef:Chef and Graph Queries | CodeCh 

[BZOJ3514]Codechef MARCH14 GERALD07加强版 题解

[BZOJ3514]Codechef MARCH14 GERALD07加强版 题解

题目地址:BZOJ:Problem 3514. — Codechef MARC 

[BZOJ3589]动态树 题解

[BZOJ3589]动态树 题解

题目地址: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;
}
[ZJOI2011]道馆之战 题解

[ZJOI2011]道馆之战 题解

题目地址:洛谷:【P4679】[ZJOI2011]道馆之战 – 洛谷、BZOJ 

[SDOI2011]染色 题解

[SDOI2011]染色 题解

题目地址:洛谷:【P2486】[SDOI2011]染色 – 洛谷、BZOJ:P 

[HAOI2015]树上操作 题解

[HAOI2015]树上操作 题解

题目地址:洛谷:【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;
}
树链剖分(轻重链剖分)原理与实现

树链剖分(轻重链剖分)原理与实现

概述 树链剖分是一种将树上的链划分为轻重链,从而实现降低复杂度的处理方式。剖分后的树,单次