EOJ3335&&hdu6162 Ch’s gift 树剖,dfs序,离线查询,主席树:各显神通

多校第九场的02

北邮出题,hdu上数据极弱

赛后发现数据是个巨型菊花图,所以裸的LCA的大暴力是可以水过的

树剖配合线段树维护最大最小值和区间和也是可以水过的

当然,本文的三种解法不包括水的解法

巨型菊花图???讲道理嘛

章鱼哥加强了本题数据挂在了EOJ3335

对于正常的数据,这里笔者找到三种解法

dfs序+离线查询

由于没有修改操作,一个显然的想法是离线处理所有问题
将询问拆成1-x,1-y,1-LCA(x,y),则处理的问题转化为从根到节点的链上的问题。
解决这个问题,我们可以在dfs时向treap插入当前的数,在退出时删除这个数,并且每次维护在该点上的答案。

每次ask求a到b的,转化为求1到a-1和1到b,将每次ask的a-1和b同归为k,对所有的k排序去重(ks[]),每次将小于k[i]的所有礼物插入线段树,然后更新有k的ask,然后处理k[i++],实现起来巨烦无比。

dfs序不懂的同学可以看看BZOJ110,本题的简化版,或者,戳这

维护一个长度为2*n的树状数组or线段树

然后分段丢进这个数据结构里面

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 7;
typedef long long LL;
int l[N], r[N];

struct gift{
    int pos;
    LL val;
    void read(int id){
        scanf("%lld", &val);
        pos = id;
    }
    bool operator < (const gift & b) const {
        return val < b.val;
    }
} gifts[N];

int ks[N * 2], K, H;
map<LL, int> hashK;
vector<int> whoAsk[N*2];
void insertK(int id, LL k){
    if (hashK.find(k) == hashK.end()) {
        hashK[k] = ++H;
        whoAsk[H].clear();
    }
    whoAsk[hashK[k]].push_back(id);
}
struct ask{
    int u, v, pos;
    LL a, b;
    vector<LL> ans;

    void read(int pos){
        this->pos = pos;
        ans.clear();
        scanf("%d%d%lld%lld", &u, &v, &a, &b);
        a--;
        ks[++K] = a, ks[++K] = b;
        insertK(pos, a);
        insertK(pos, b);
    }
    inline void print(){
        printf("%lld", abs(ans[1] - ans[0]));
    }
} asks[N];

struct binaryIndexTree{
    LL val[N * 2];
    int n;
    inline void build(int n){
        this->n = n;
        memset(val, 0, sizeof(val));
    }
    inline void add(int k, LL num){
        for (;k <= n; k += k&-k) val[k] += num;
    }
    LL sum(int k){
        if (k == 0) return 0;
        LL sum = 0;
        for (; k; k -= k&-k) sum += val[k];
        return sum;
    }
} TT ;

struct segTree{
    LL tree[N * 6];
    int M;
    inline void build(int n){
        M = 1; for(;M<n;) M<<=1; if(M!=1)M--;
        memset(tree, sizeof(tree), 0);
    }
    void add(int t, LL x){
        for (tree[t+=M]+=x, t>>=1; t; t>>=1){
            tree[t] = tree[t<<1] + tree[t<<1^1];
        }
    }
    LL sum(int l, int r){
        if (l > r || r == 0) return 0;
        LL ans = 0;
        for (l+=M-1,r+=M+1; l^r^1; l>>=1,r>>=1){
            if (~l&1) ans += tree[l^1];
            if ( r&1) ans += tree[r^1];
        }
        return ans;
    }
} T;

struct graph{
    struct Edge{
        int from, to, nxt;
        Edge(){}
        Edge(int u, int v, int n):from(u), to(v), nxt(n){}
    } edges[N * 2];
    static const int LCADEP = 17;
    int n, E, head[N];
    int top, dep[N], fa[N][LCADEP + 1];

    inline void AddEdge(int f, int t){
        edges[++E] = Edge(f, t, head[f]);
        head[f] = E;
    }
    inline void Init(int n){
        this -> n = n ; E = -1; top = 0; dep[0] = 0;
        for (int i = 0; i <= n; i++) head[i] = -1;
        memset(fa, 0, sizeof(fa));
    }

    void dfs(int u, int pre){
        l[u] = ++top;
        //printf("l[%d] = %d
", u, top);
        fa[u][0] = pre;
        dep[u] = dep[pre] + 1;
        for (int i = 1; i <= LCADEP; i++){
            if (dep[u] < (1<<i)) break;
            fa[u][i] = fa[fa[u][i-1]][i-1];
        }
        for (int i = head[u]; i != -1; i = edges[i].nxt){
            if (edges[i].to != pre) dfs(edges[i].to, u);
        }
        r[u] = ++top;
        //printf("r[%d] = %d
", u, top);
    }

    int lca(int x, int y){
        if (dep[x] < dep[y]) swap(x,y);
        int t = dep[x] - dep[y];
        for (int i = 0; i <= LCADEP; i++) if ((1<<i) & t) x = fa[x][i];
        for (int i = LCADEP; i >= 0; i--) if (fa[x][i] != fa[y][i]){
            x = fa[x][i]; y = fa[y][i];
        }
        return x==y ? x : fa[x][0];
    }

    void solve(ask &a){
        int u = a.u, v = a.v;
        int f = lca(u, v);
        LL ans = T.sum(1, l[u]) + T.sum(1, l[v]) - T.sum(1, l[f]) - T.sum(1, l[fa[f][0]]);
        //LL ans = T.sum(l[u]) + T.sum(l[v]) - T.sum(l[f]) - T.sum(l[fa[f][0]]);
        a.ans.push_back(ans);
    }
} g ;

int main () {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    int n, m, u, v;
    for (; cin >> n >> m;) {
        for(int i = 1; i <= n; i++) gifts[i].read(i);
        sort(gifts + 1, gifts + n+1);
        g.Init(n);
        for(int i = 0; i < n - 1; i++) {
            scanf("%d%d", &u, &v);
            g.AddEdge(u, v);
            g.AddEdge(v, u);
        }
        g.dfs(1, 0);

        T.build(n*2);
        K = 0, H = 0;
        hashK.clear();
        for (int i = 1; i <= m; i++) asks[i].read(i);
        sort(ks + 1, ks + K+1);
        K = unique(ks + 1, ks + K+1) - (ks + 1);

        int cur = 1;
        for (int i = 1; i <= K; i++){
            //printf("ks[%d] = %d
", i, ks[i]);
            for (int &j = cur; j  <= n; j++){
                if (gifts[j].val > ks[i]) break;
                //printf("gifts[%d].val = %d, pos = %d, [%d, %d]
", j, gifts[j].val, gifts[j].pos, l[gifts[j].pos], r[gifts[j].pos]);
                T.add(l[gifts[j].pos], gifts[j].val);
                T.add(r[gifts[j].pos],-gifts[j].val);
            }
            int kk = hashK[ks[i]];
            for (int j = 0; j < whoAsk[kk].size(); j++){
                ask &a = asks[whoAsk[kk][j]];
                g.solve(a);
            }
        }

        for (int i = 1; i <= m; i++){
            asks[i].print();
            putchar(i==m ? '
' : ' ');
        }
    }
    return 0;
}c++


树链剖分+离线查询

这样维护的数据结构大小为n,而且不用写lca

当然也可以将所有的查询和点权排序,用树链剖分做这个题,在线段树上面插入就ok。

树剖

树剖其实是最直接最粗暴的将树映射到了线段树上,没有了dfs序的加法减法还有lca,各种前缀和

直接寻找路径

#include <map>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 3e5 + 7;
typedef long long LL;
int n;
struct gift{
    int pos, val;
    void read(int id){
        scanf("%d", &val);
        pos = id;
    }
    bool operator < (const gift & b) const {
        return val < b.val;
    }
} gifts[N];

struct segmentTree{
    #define lc (t<<1)
    #define rc (t<<1^1)
    LL sum[N];
    int M;
    inline void build(int n){
        M = 1; for(;M<n;)M<<=1; if(M!=1)M--;
        memset(sum, sizeof(sum), 0);
    }
    void add(int t, LL x){
        for (sum[t+=M]+=x, t>>=1; t; t>>=1){
            sum[t] = sum[lc] + sum[rc];
        }
    }
    LL query(int l, int r){
        LL ans = 0;
        for (l+=M-1,r+=M+1; l^r^1; l>>=1,r>>=1){
            if (~l&1) ans += sum[l^1];
            if ( r&1) ans += sum[r^1];
        }
        return ans;
    }
} T;

struct TreeChain{
    struct Edge{
        int from, to, nxt;
        Edge(){}
        Edge(int u, int v, int n):
            from(u), to(v), nxt(n){}
    }edges[N];
    int n, E, head[N];

    int tim;
    int siz[N]; //用来保存以x为根的子树节点个数
    int top[N]; //用来保存当前节点的所在链的顶端节点
    int son[N]; //用来保存重儿子
    int dep[N]; //用来保存当前节点的深度
    int fa[N];  //用来保存当前节点的父亲
    int tid[N]; //用来保存树中每个节点剖分后的新编号,线段树
    int Rank[N];//tid反向数组,不一定需要

    inline void AddEdge(int f, int t){
        edges[++E] = Edge(f, t, head[f]);
        head[f] = E;
    }
    inline void Init(int n){
        tim = 0;
        this -> n = n ; E = -1;
        for (int i = 0; i <= n; i++) head[i] = -1;
        for (int i = 0; i <= n; i++) son[i] = -1;
    }

    void dfs1(int u, int father, int d){
        dep[u] = d;
        fa[u] = father;
        siz[u] = 1;
        int nxt;
        for(int i = head[u]; i != -1; i = nxt){
            Edge &e = edges[i]; nxt = e.nxt;
            if (e.to == father) continue;
            dfs1(e.to, u, d + 1);
            siz[u] += siz[e.to];
            if(son[u]==-1 || siz[e.to] > siz[son[u]]) son[u] = e.to;
        }
    }
    void dfs2(int u, int tp){
        top[u] = tp;
        tid[u] = ++tim;
        Rank[tid[u]] = u;
        if (son[u] == -1) return;
        dfs2(son[u], tp);
        int nxt;
        for(int i = head[u]; i != -1; i = nxt){
            Edge &e = edges[i]; nxt = e.nxt;
            if(e.to == son[u] || e.to == fa[u]) continue;
            dfs2(e.to, e.to);
        }
    }
    LL query(int u, int v){
        int f1 = top[u], f2 = top[v];
        LL tmp = 0;
        for (; f1 != f2;){
            if (dep[f1] < dep[f2]){
                swap(f1, f2);
                swap(u, v);
            }
            tmp += T.query(tid[f1], tid[u]);
            u = fa[f1]; f1 = top[u];
        }
        if (dep[u] > dep[v]) swap(u, v);
        return tmp + T.query(tid[u], tid[v]);
    }
} g ;

int ks[N], K, H;
map<int, int> hashK;
vector<int> whoAsk[N*2];
void insertK(int id, int k){
    if (hashK.find(k) == hashK.end()) {
        hashK[k] = ++H;
        whoAsk[H].clear();
    }
    whoAsk[hashK[k]].push_back(id);
}
struct ask{
    int u, v, a, b, pos;
    vector<LL> ans;
    void read(int pos){
        this->pos = pos;
        ans.clear();
        scanf("%d%d%d%d", &u, &v, &a, &b);
        a--;
        ks[++K] = a, ks[++K] = b;
        insertK(pos, a);
        insertK(pos, b);
    }
} asks[N];

int main () {
    freopen("in.txt", "r", stdin);
    int m, u, v;
    while(cin >> n >> m) {
        for(int i = 1; i <= n; i++) {
            gifts[i].read(i);
        }
        sort(gifts + 1, gifts + n+1);
        g.Init(n);
        for(int i = 0; i < n - 1; i++) {
            scanf("%d%d", &u, &v);
            g.AddEdge(u, v);
            g.AddEdge(v, u);
        }
        g.dfs1(1, -1, 0);
        g.dfs2(1, 1);

        T.build(n);
        K = 0, H = 0;
        hashK.clear();
        for (int i = 1; i <= m; i++) asks[i].read(i);
        sort(ks + 1, ks + K+1);
        K = unique(ks + 1, ks + K+1) - (ks + 1);

        int cur = 1;
        for (int i = 1; i <= K; i++){
            for (int &j = cur; j  <= n; j++){
                if (gifts[j].val > ks[i]) break;
                T.add(g.tid[gifts[j].pos], gifts[j].val);
            }
            int kk = hashK[ks[i]];
            for (int j = 0; j < whoAsk[kk].size(); j++){
                ask &a = asks[whoAsk[kk][j]];
                a.ans.push_back(g.query(a.u, a.v));
            }
        }

        for (int i = 1; i <= m; i++){
            printf("%lld", abs(asks[i].ans[1] - asks[i].ans[0]));
            putchar(i==m ? '
' : ' ');
        }
    }
    return 0;
}

直接套个主席树

话说,离线查询那么难写,都是不会主席树的下策,主席树会了谁管那么多

主席树刚刚开坑,上篇blog写了一些,这份代码在hdu上可以通过

还没有通过章鱼哥的加强数据,,待续

29日中午更新:
通过了章鱼哥的数据,因为

这里写图片描述

一开始写了n,导致离散化血崩

#include <map>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 7;
typedef long long LL;
LL gift[N], Rank[N];//节点权值和离散化

struct ChairTree{
    #define sum(x) tree[x].sum
    #define lson tree[rt].lc, tree[rt1].lc, l, m
    #define rson tree[rt].rc, tree[rt1].rc, m+1, r
    struct node{
        int lc, rc;
        LL sum;
    } tree[N * 30];
    int n, root[N], cnt;

    inline void build(int _n){
        n = _n; cnt = 0;
    }

    void add(int pos, LL val, int &rt, int rt1, int l, int r){
        tree[rt = ++cnt] = tree[rt1];
        tree[rt].sum += val;
        if (l == r) return;
        int m = (l + r) >> 1;
        if (pos <= m) add(pos, val, lson);
        else add(pos, val, rson);
    }

    LL query(int L, int R, int rt, int rt1, int l, int r){
        if (L <= l && r <= R) return sum(rt1) - sum(rt);
        if (sum(rt1) == 0) return 0;
        if (sum(rt1) == sum(rt)) return 0;
        LL ans = 0;
        int m = (l + r) >> 1;
        if (L <= m) ans += query(L, R, lson);
        if (m <  R) ans += query(L, R, rson);
        return ans;
    }
    #undef sum(x)
    #undef lson
    #undef rson
} T;

struct graph{
    struct Edge{
        int from, to, nxt;
        Edge(){}
        Edge(int u, int v, int n):from(u), to(v), nxt(n){}
    } edges[N * 2];
    static const int LCADEP = 17;
    int n, E, head[N];
    int top, dep[N], fa[N][LCADEP + 1];

    inline void AddEdge(int f, int t){
        edges[++E] = Edge(f, t, head[f]);
        head[f] = E;
    }
    inline void Init(int n){
        this -> n = n ; E = -1; top = 0; dep[0] = 0;
        for (int i = 0; i <= n; i++) head[i] = -1;
        memset(fa, 0, sizeof(fa));
    }

    void dfs(int u, int pre){
        T.add(gift[u], Rank[gift[u]], T.root[u], T.root[pre], 1, T.n);
        fa[u][0] = pre;
        dep[u] = dep[pre] + 1;
        for (int i = 1; i <= LCADEP; i++){
            if (dep[u] < (1<<i)) break;
            fa[u][i] = fa[fa[u][i-1]][i-1];
        }
        for (int i = head[u]; i != -1; i = edges[i].nxt){
            if (edges[i].to != pre) dfs(edges[i].to, u);
        }
    }

    int lca(int x, int y){
        if (dep[x] < dep[y]) swap(x,y);
        int t = dep[x] - dep[y];
        for (int i = 0; i <= LCADEP; i++) if ((1<<i) & t) x = fa[x][i];
        for (int i = LCADEP; i >= 0; i--) if (fa[x][i] != fa[y][i]){
            x = fa[x][i]; y = fa[y][i];
        }
        return x==y ? x : fa[x][0];
    }

    LL query(int u, int v, int L, int R){
        int f = lca(u, v);
        LL ans = 0;
        ans += T.query(L, R, T.root[f], T.root[u], 1, T.n);
        ans += T.query(L, R, T.root[fa[f][0]], T.root[v], 1, T.n);
        return ans;
    }
} g ;

int main () {
    //freopen("in.txt", "r", stdin);
    int n, q, u, v;
    for (LL a, b; ~scanf("%d%d", &n, &q);) {
        for(int i = 1; i <= n; i++) {
            scanf("%lld", &gift[i]);
            Rank[i] = gift[i];
        }
        sort(Rank + 1, Rank + n+1);
        int un = unique(Rank + 1, Rank + n+1) - (Rank+1);
        for (int i = 1; i <= n; i++){
            gift[i] = lower_bound(Rank + 1, Rank + un+1, gift[i]) - Rank;
        }

        g.Init(n);
        for(int i = 0; i < n - 1; i++) {
            scanf("%d%d", &u, &v);
            g.AddEdge(u, v);
            g.AddEdge(v, u);
        }
        T.build(un);
        g.dfs(1, 0);

        for (; q--;){
            scanf("%d%d%lld%lld", &u, &v, &a, &b);
            int aa = lower_bound(Rank+1, Rank + un+1, a) - Rank;
            if (Rank[aa] < a) aa++;
            int bb = lower_bound(Rank+1, Rank + un+1, b) - Rank;
            if (bb > un || Rank[bb] > b) bb--;
            printf("%lld", g.query(u, v, aa, bb));
            putchar(q==0 ? '
' : ' ');
        }
    }
    return 0;
}


原文地址:https://www.cnblogs.com/cww97/p/12349345.html