zoukankan      html  css  js  c++  java
  • Boss Bo (主席树)

    主要想法:假设给你足够时间,那么就可以对每个点建议一颗线段树来查询了,但是需要将点全部按照某个特定的序列存进线段树,如代码是以树的深搜顺序作为指定顺序,这样以来我们既可以将数据查询分成诺干个区间进行查询了,减少了大量查询。那么这样你都知道了,其实只需要利用主席树将每个点的情况都存进去主席树里面,因为对于相邻的点来说有着太多的重复情况了,所以我们可以利用这个数据结构来优化时间。

    #include<bits/stdc++.h>
    #define mm ((l + r) >> 1)
    #define inf 0x3f3f3f3f
    #define maxn 100070
    #define maxm 5000700
    using namespace std;
    
    vector<int>Gra[maxn];
    pair<int, int>a[maxn];
    int st[maxn], ed[maxn], dep[maxn], lab[maxn], rt[maxn];
    int sum[maxm], mi[maxm], ma[maxm], add[maxm], ch[maxm][2];
    int N, Q, ntr, tot, lastans, bb, aa;
    
    ///求深度、设置编号顺序
    void dfs1(int u, int no, int d){
        dep[u] = d;
        st[u] = ++ntr;
        lab[ntr] = u;
        for(int i = Gra[u].size() - 1; i >= 0; i--){
            int v = Gra[u][i];
            if(v != no) dfs1(v, u, d + 1);
        }
        ed[u] = ntr;
    }
    
    ///数据跟新
    void push_up(int rt, int l, int r){
        sum[rt] = sum[ch[rt][0]] + sum[ch[rt][1]] + (r - l + 1) * add[rt];
        ma[rt] = max(ma[ch[rt][0]], ma[ch[rt][1]]) + add[rt];
        mi[rt] = min(mi[ch[rt][0]], mi[ch[rt][1]]) + add[rt];
    }
    
    ///建树
    int buildTree(int l, int r){
        int rt = ++tot;
        if(l == r){
            sum[rt] = ma[rt] = mi[rt] = dep[lab[l]] - 1;
            ch[rt][0] = ch[rt][1] = add[rt] = 0;
            return rt;
        }
        sum[rt] = ma[rt] = mi[rt] = add[rt] = 0;
        ch[rt][0] = buildTree(l, mm);
        ch[rt][1] = buildTree(mm + 1, r);
        push_up(rt, l, r);
        return rt;
    }
    
    ///更新区间、 更新值、 实际区间、 旧的数据点
    int update(int ll, int rr, int v, int l, int r, int old){
        int rt = ++tot;
        sum[rt] = sum[old]; add[rt] = add[old];
        ma[rt] = ma[old]; mi[rt] = mi[old];
        ch[rt][0] = ch[old][0]; ch[rt][1] = ch[old][1];
        if(ll == l && rr == r){
            add[rt] += v;
            sum[rt] += (rr - ll + 1) * v;
            ma[rt] += v;
            mi[rt] += v;
            return rt;
        }
        if(rr <= mm) ch[rt][0] = update(ll, rr, v, l, mm, ch[old][0]);
        else if(ll > mm) ch[rt][1] = update(ll, rr, v, mm + 1, r, ch[old][1]);
        else{
            ch[rt][0] = update(ll, mm, v, l, mm, ch[old][0]);
            ch[rt][1] = update(mm + 1, rr, v, mm + 1, r, ch[old][1]);
        }
        push_up(rt, l, r);
        return rt;
    }
    
    ///遍历所有点为每个点在主席树中植入一个版本。关于以该点根的情况。
    void dfs2(int u, int no){
        for(int i = Gra[u].size() - 1; i >= 0; i --){
            int v = Gra[u][i];
            if(v == no) continue;
            rt[v] = update(st[v], ed[v], -1, 1, N, rt[u]);
            if(st[v] > 1) rt[v] = update(1, st[v] - 1, 1, 1, N, rt[v]);
            if(ed[v] < N) rt[v] = update(ed[v] + 1, N, 1, 1, N, rt[v]);
            dfs2(v, u);
        }
    }
    
    ///相当于查询关于某个点版本的线段树
    int query(int ll, int rr, int t, int l, int r, int rt){
        if(l == ll && r == rr){
            if(t == 1) return sum[rt];
            else if(t == 2) return mi[rt];
            else return ma[rt];
        }
        if(rr <= mm){
            int ret = query(ll, rr, t, l, mm, ch[rt][0]);
            if(t ==  1) return ret + (rr - ll + 1) * add[rt];
            else return ret + add[rt];
        }else if(ll > mm){
            int ret = query(ll, rr, t, mm + 1, r, ch[rt][1]);
            if(t == 1) return ret + (rr - ll + 1) * add[rt];
            else return ret + add[rt];
        }else{
            int retl = query(ll, mm, t, l, mm, ch[rt][0]);
            int retr = query(mm + 1, rr, t, mm + 1, r, ch[rt][1]);
            if(t == 1) return retl + retr + (rr - ll + 1) * add[rt];
            if(t == 2) return min(retl, retr) + add[rt];
            if(t == 3) return max(retl, retr) + add[rt];
        }
    }
    
    int main(){
        while(~scanf("%d%d",&N,&Q)){
            for(int i = 1; i <= N; i ++) Gra[i].clear();
            for(int i = 1; i < N; i ++){
                scanf("%d%d",&aa,&bb);
                Gra[aa].push_back(bb);Gra[bb].push_back(aa);
            }
            lastans = tot = ntr = 0;
            dfs1(1, -1, 1);
            rt[1] = buildTree(1, N);
            dfs2(1, -1);
    
            int K, T, P, x;
            while(Q -- ){
                scanf("%d%d%d", &K, &P, &T);
                P = (lastans + P) % N + 1;
                bool rootsign = false;
                for(int i = 0; i < K; i ++){
                    scanf("%d", &x);
                    a[i] = make_pair(st[x], ed[x]);
                    rootsign = x == 1 ? true : rootsign;
                }
                if(rootsign){
                    printf("-1
    ");
                    lastans = 0;
                    continue;
                }
                sort(a, a + K);
                a[K ++] = make_pair(N + 1, N + 1);
                ///排完序之后按照中间空开的区间查询关于某个点的某种数据
                if(T == 1) lastans = 0;
                else if(T == 2) lastans = inf;
                else lastans = -inf;
                int pre = 1;
                for(int i = 0; i < K; i ++){
                    if(pre < a[i].first){
                        int tmp = query(pre, a[i].first - 1, T, 1, N, rt[P]);
                        if(T == 1) lastans += tmp;
                        else if(T == 2) lastans = min(lastans, tmp);
                        else lastans = max(lastans, tmp);
                    }
                    pre = max(pre, a[i].second + 1);
                }
                printf("%d
    ",lastans);
            }
        }
        return 0;
    }
    more crazy more get!
  • 相关阅读:
    WinForm被遮挡的控件解决方案
    IC卡资料
    水晶报表2008部署
    打造最强的VC6
    SqlServer Case
    using namespace std
    非接触式IC智能(射频)卡
    删除VS2005插件

    SQLServer2005数据库自动备份
  • 原文地址:https://www.cnblogs.com/wethura/p/9801743.html
Copyright © 2011-2022 走看看