zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
  • 相关阅读:
    asp.net如何实现删除文件的操作? (转)
    开始计算机英语的学习,先把这里当生词本用了。
    CSS Box Model 盒子模型
    生成网站快捷方式
    Microsoft .NET Framework 3.5 sp1离线安装解决方案
    asp.net生成网站快捷方式
    https://mail.google.com/tasks/ig?pli=1
    Sql server DATEDIFF DATEADD
    局域网 跨数据库 访问数据库
    asp.net 生成网站快捷方式
  • 原文地址:https://www.cnblogs.com/cww97/p/7533937.html
Copyright © 2011-2022 走看看