作者: KSkun

[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;
}
[SDOI2011]染色 题解

[SDOI2011]染色 题解

题目地址:洛谷:【P2486】[SDOI2011]染色 – 洛谷、BZOJ:Problem 2243. — [SDOI2011]染色

题目描述

给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),
如“112221”由3段组成:“11”、“222”和“1”。
请你写一个程序依次完成这m个操作。

输入输出格式

输入格式:
第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面 行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面 行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

输出格式:
对于每个询问操作,输出一行答案。

输入输出样例

输入样例#1:

6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5

输出样例#1:

3
1
2

说明

数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。

题解

树链信息自然就是树剖。线段树维护当前区间左右端颜色以及这段区间中间有几段。合并检查拼接点是否同样颜色。树剖上跳的过程类似,实现见代码。

代码

// Code by KSkun, 2018/3
#include <cstdio>

#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 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;
}

inline bool isop(char c) {
    return c == 'Q' || c == 'C';
}

inline char readop() {
    char c;
    while(!isop(c = fgc()));
    return c;
}

const int MAXN = 100005;

int n, m, w[MAXN], ut, vt, at, bt, ct;
char op;
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 {
    int l, r, tot;
    bool tag;
} sgt[MAXN << 2];

inline void pushdown(int o, int l, int r) {
    int lch = o << 1, rch = (o << 1) | 1;
    if(sgt[o].tag) {
        sgt[lch].l = sgt[lch].r = sgt[rch].l = sgt[rch].r = sgt[o].l;
        sgt[lch].tag = sgt[rch].tag = true;
        sgt[lch].tot = sgt[rch].tot = 1;
        sgt[o].tag = false;
    }
}

inline void merge(Node &rt, Node ls, Node rs) {
    rt.l = ls.l;
    rt.r = rs.r;
    rt.tot = ls.tot + rs.tot;
    if(ls.r == rs.l) rt.tot--;
}

inline void build(int o, int l, int r) {
    if(l == r) {
        sgt[o].l = sgt[o].r = w[ptn[l]];
        sgt[o].tot = 1;
        return;
    }
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    build(lch, l, mid);
    build(rch, mid + 1, r);
    merge(sgt[o], sgt[lch], sgt[rch]);
}

inline void modify(int o, int l, int r, int ll, int rr, int v) {
    if(l == ll && r == rr) {
        sgt[o].l = sgt[o].r = v;
        sgt[o].tot = 1;
        sgt[o].tag = true;
        return;
    }
    pushdown(o, l, r);
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    if(rr <= mid) {
        modify(lch, l, mid, ll, rr, v);
    } else if(ll > mid) {
        modify(rch, mid + 1, r, ll, rr, v);
    } else {
        modify(lch, l, mid, ll, mid, v);
        modify(rch, mid + 1, r, mid + 1, rr, v);
    }
    merge(sgt[o], sgt[lch], sgt[rch]);
}

inline Node query(int o, int l, int r, int ll, int rr) {
    if(l == ll && r == rr) {
        return sgt[o];
    }
    pushdown(o, l, r);
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    if(rr <= mid) {
        return query(lch, l, mid, ll, rr);
    } else if(ll > mid) {
        return query(rch, mid + 1, r, ll, rr);
    } else {
        Node ls = query(lch, l, mid, ll, mid), rs = query(rch, mid + 1, r, mid + 1, rr), res;
        merge(res, ls, rs);
        return res;
    }
}

inline void modify(int u, int v, int c) {
    int tu = top[u], tv = top[v];
    while(tu != tv) {
        if(dep[tu] > dep[tv]) {
            std::swap(tu, tv);
            std::swap(u, v);
        }
        modify(1, 1, n, dfn[tv], dfn[v], c);
        v = fa[tv];
        tv = top[v];
    }
    if(dep[u] > dep[v]) std::swap(u, v);
    modify(1, 1, n, dfn[u], dfn[v], c);
}

inline int query(int u, int v) {
    Node t;
    int res = 0, cu = -1, cv = -1, tu = top[u], tv = top[v];
    while(tu != tv) {
        if(dep[tu] > dep[tv]) {
            std::swap(tu, tv);
            std::swap(u, v);
            std::swap(cu, cv);
        }
        t = query(1, 1, n, dfn[tv], dfn[v]);
        res += t.tot;
        if(t.r == cv) res--;
        cv = t.l;
        v = fa[tv];
        tv = top[v];
    }
    if(dep[u] > dep[v]) {
        std::swap(u, v);
        std::swap(cu, cv); 
    }
    t = query(1, 1, n, dfn[u], dfn[v]);
    res += t.tot;
    if(t.l == cu) res--;
    if(t.r == cv) res--;
    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 = readop();
        at = readint();
        bt = readint();
        if(op == 'Q') {
            printf("%d\n", query(at, bt));
        }
        if(op == 'C') {
            ct = readint();
            modify(at, bt, ct);
        }
    }
    return 0;
}
[HDU5385]The path 题解

[HDU5385]The path 题解

题目地址:HDUOJ:Problem – 5385

题目描述

You have a connected directed graph.Let d(x) be the length of the shortest path from 1 to x.Specially d(1)=0.A graph is good if there exist x satisfy d(1)<d(2)<….d(x)>d(x+1)>…d(n).Now you need to set the length of every edge satisfy that the graph is good.Specially,if d(1)<d(2)<..d(n),the graph is good too.
The length of one edge must ∈ [1,n]
It’s guaranteed that there exists solution.
给你一个有向图,你要确定每条边的边权,使得存在1到每个点的最短路距离d满足d(1)<d(2)<….d(x)>d(x+1)>…d(n)。

输入输出格式

输入格式:
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
The first line contains two integers n and m,the number of vertexs and the number of edges.Next m lines contain two integers each, ui and vi (1≤ui,vi≤n), indicating there is a link between nodes ui and vi and the direction is from ui to vi.
∑n≤3∗10^5,∑m≤6∗10^5
1≤n,m≤10^5

输出格式:
For each test case,print m lines.The i-th line includes one integer:the length of edge from ui to vi

输入输出样例

输入样例#1:

2
4 6
1 2
2 4
1 3
1 2
2 2
2 3
4 6
1 2
2 3
1 4
2 1
2 1
2 1

输出样例#1:

1
2
2
1
4
4
1
1
3
4
4
4

题解

其实这道题如果能想到Prim算法的流程就好做了。Prim是确定一个点的d为0,然后向外扩展到当前选定点集距离最小的点,扩展完毕以后就是一棵最短路树/最小生成树。那么我们也来构造这样一棵最短路树/最小生成树,使得树边满足题目要求且非树边边权极大就可以了。
我们确定1为树根,d(1)=0,然后从最小号点和最大号点开始往里加点,每加一个点就给它的出边和相邻点打标记,表示这些边/点以后应当计算。
虽然数据范围长得很像O(n \log n),但其实算法是O(m)的。

代码

// 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 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 = 600005;

struct Edge {
    int u, v, id;
};

int T, n, m, ut, vt, dis[MAXN];
std::vector<Edge> gra[MAXN], edges;
bool vis[MAXN], ans[MAXN];

inline void work(int u, int d) {
    dis[u] = d;
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i].v;
        if(vis[v]) continue;
        vis[v] = true;
        ans[gra[u][i].id] = true;
    }
}

int main() {
    T = readint();
    while(T--) {
        n = readint(); m = readint();
        memset(dis, 0, sizeof(dis));
        memset(ans, 0, sizeof(ans));
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= n; i++) gra[i].clear();
        edges.clear();
        for(int i = 1; i <= m; i++) {
            ut = readint(); vt = readint();
            gra[ut].push_back(Edge {ut, vt, i});
            edges.push_back(Edge {ut, vt, i});
        }
        int l = 1, r = n, d = 0;
        vis[l] = true;
        while(l <= r) {
            if(vis[l]) work(l++, d++);
            if(vis[r]) work(r--, d++);
        }
        for(int i = 1; i <= m; i++) {
            if(ans[i]) {
                printf("%d\n", std::abs(dis[edges[i - 1].u] - dis[edges[i - 1].v]));
            } else {
                printf("%d\n", n);
            }
        }
    }
    return 0;
}