zoukankan      html  css  js  c++  java
  • 旅行(树剖+主席树)

    旅行(树剖+主席树)

    给定一个n(<=1e5)的图,m(<=1e5)个操作,每次标记第ci个点,或者询问a到b的路径中,在第y个操作之后没有被标记的第k个点。

    树剖+主席树维护重链。就说三点(说多了都是泪):1. 线段树上二分要注意min max。2. windows的缺省栈值只有4m,所以递归1e5层铁定爆栈了(我的程序3e4层就爆了)。 3. 暴力算法,通常,隐藏的很深。

    ……

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int maxn=1e5+5, INF=0x3f3f;
    int n, m, root;
    struct Edge{
        int to, nxt;
    }e[maxn*2];
    int cnte, fir[maxn];
    void addedge(int x, int y){
        Edge &ed=e[++cnte];
        ed.to=y; ed.nxt=fir[x]; fir[x]=cnte; }
    
    int dep[maxn], siz[maxn], son[maxn], fa[maxn];
    void dfs1(int now, int fat, int depth){
        dep[now]=depth; siz[now]=1;
        int maxm=0; fa[now]=fat;
        for (int i=fir[now]; i; i=e[i].nxt){
            dfs1(e[i].to, now, depth+1);
            siz[now]+=siz[e[i].to];
            if (siz[e[i].to]>maxm){
                maxm=siz[e[i].to]; son[now]=e[i].to; }
        }
    }
    
    int top[maxn], id[maxn], ori[maxn], cnt;  //id[i]:结点i在主席树中位置的编号
    void dfs2(int now){
        id[now]=++cnt; ori[cnt]=now;
        for (int i=fir[now]; i; i=e[i].nxt)
            if (e[i].to==son[now]){
                top[e[i].to]=top[now];
                dfs2(e[i].to); }
        for (int i=fir[now]; i; i=e[i].nxt)
            if (e[i].to!=son[now]){
                top[e[i].to]=e[i].to;
                dfs2(e[i].to);
            }
    }
    
    int rt[maxn], cntnode;
    struct Node{
        int x, l, r;  //x表示有多少点*没有*被标记过
    }node[maxn*20];
    //now:当前主席树的结点编号 ori:前一个主席树的同位点编号
    void ins(int &now, int ori, int l, int r, int pos){
        now=++cntnode; node[now]=node[ori];
        if (l==r){ node[now].x=1; return;  }
        int mid=(l+r)>>1;
        if (pos<=mid) ins(node[now].l, node[ori].l, l, mid, pos);
        else ins(node[now].r, node[ori].r, mid+1, r, pos);
        ++node[now].x;  //保证区间内被标的点数+1
    }
    int query(int now, int l, int r, int L, int R){
        if (!now) return 0;
        if (l>r) return 0;
        if (r<L||l>R) return 0;
        if (l>=L&&r<=R){ return node[now].x; }
        int mid=(l+r)>>1, ans=0;
        if (mid>=L) ans+=query(node[now].l, l, mid, L, R);
        if (mid<R) ans+=query(node[now].r, mid+1, r, L, R);
        return ans;
    }
    
    struct Query{  //表示能被询问到的区间
        Query(int x=0, int y=0, int d=0){ l=x; r=y; dir=d; }
        int l, r, dir;  //l和r都是主席树上的点
    }q[maxn];  //若dir=1表示是上升的,dir=-1表示是下降的
    int cntq, latest;
    
    void jump(int x, int y, int flag){  //处理出所有询问区间
        cntq=0; int lca, ox=x, oy=y, lx, ly;
        if (flag&&(x==y||fa[x]==y||fa[y]==x)) return;
        while (top[x]!=top[y]){
            if (dep[top[x]]>dep[top[y]]){
                q[cntq++]=Query(id[top[x]], id[x], 1);
                lx=top[x]; x=fa[top[x]];
            }
            if (top[x]==top[y]) break;
            if (dep[top[x]]<=dep[top[y]]){
                q[cntq++]=Query(id[top[y]], id[y], -1);
                ly=top[y]; y=fa[top[y]];
            }
        }
        if (dep[x]>dep[y]) q[cntq++]=Query(id[y], id[x], 1), lca=y;
        else q[cntq++]=Query(id[x], id[y], -1), lca=x;
        if (!flag) return;
        if (ox==lca){
            if(x==y) jump(ly, fa[oy], 0); else jump(son[ox], fa[oy], 0);
        } else if (oy==lca){
            if (x==y) jump(fa[ox], lx, 0); else jump(fa[ox], son[oy], 0);
        } else jump(fa[ox], fa[oy], 0);
    }
    
    //二分查找线段树区间上从左到右第k个未标记点
    int div1(int nowb, int nowa, int l, int r, int L, int R, int k){
        if (l==r) return l;
        int mid=(l+r)>>1;
        int lans=query(node[nowb].l, l, mid, L, R)
            -query(node[nowa].l, l, mid, L, R);  //左边有多少标记点
        lans=mid-max(l, L)+1-lans; if (mid<L) lans=0;  //注意max这里是坑!
        if (lans>=k) return div1(node[nowb].l, node[nowa].l, l, mid, L, R, k);
        else return div1(node[nowb].r, node[nowa].r, mid+1, r, L, R, k-lans);
    }
    
    //二分查找线段树区间上从右到左第k个未标记点
    int div2(int nowb, int nowa, int l, int r, int L, int R, int k){
        if (l==r) return l;
        int mid=(l+r)>>1;
        int rans=query(node[nowb].r, mid+1, r, L, R)
            -query(node[nowa].r, mid+1, r, L, R);  //右边有多少标记点
        rans=min(r, R)-mid-rans; if (mid>=R) rans=0;
        if (rans>=k)  return div2(node[nowb].r, node[nowa].r, mid+1, r, L, R, k);
        else return div2(node[nowb].l, node[nowa].l, l, mid, L, R, k-rans);
    }
    
    int solve(int k, int y, int now){  //已知这些顺序的区间问第k个未标记点
        for (int i=0; i<cntq; ++i){
            if (q[i].dir==-1) continue;
            int t=query(rt[now], 1, n, q[i].l, q[i].r)
                -query(rt[y], 1, n, q[i].l, q[i].r);  //整段区间上的标记点个数
            t=q[i].r-q[i].l+1-t;
            if (k<=t) return ori[div2(rt[now], rt[y], 1, n, q[i].l, q[i].r, k)];
            k-=t;
        }
        for (int i=cntq-1; i>=0; --i){  //倒着处理区间
            if (q[i].dir==1) continue;
            int t=query(rt[now], 1, n, q[i].l, q[i].r)
                -query(rt[y], 1, n, q[i].l, q[i].r);
            t=q[i].r-q[i].l+1-t;
            if (k<=t) return ori[div1(rt[now], rt[y], 1, n, q[i].l, q[i].r, k)];
            k-=t;
        }
        return -1;
    }
    
    int main(){
        scanf("%d", &n); int t;
        for (int i=1; i<=n; ++i){
            scanf("%d", &t);
            if (!t) root=i; else addedge(t, i);
        }
        dfs1(root, 0, 0); top[root]=root; dfs2(root);
        int op, a, b, c, k, y;
        scanf("%d", &m);
        for (int i=1; i<=m; ++i){
            scanf("%d", &op);
            if (op==1){
                scanf("%d", &c);
                ins(rt[i], rt[i-1], 1, n, id[c]); continue; }
            rt[i]=rt[i-1];
            scanf("%d%d%d%d", &a, &b, &k, &y);
            jump(a, b, 1);
            printf("%d
    ", solve(k, y, i));  //走y+1到i时刻之间没有被标记的点
        }
        return 0;
    }
    
  • 相关阅读:
    System.Runtime.InteropServices.COMException (0x800706BA) 解决方法
    Win7 不能安装 msi 解决办法
    note 1 对象和数据类型
    note 0 Python介绍及Python IDE环境安装 Spyder with Anaconda
    无法获得锁 /var/lib/dpkg/lock
    keil5 MDK 链接报错 Error: L6410W 解决
    手动卸载CAD 删除残留文件 清理遗留的文件
    keil5 MDK warning:registered ARM compiler version not found in path
    干掉hao123劫持浏览器主页
    Win7 无法访问Installer服务
  • 原文地址:https://www.cnblogs.com/MyNameIsPc/p/9352007.html
Copyright © 2011-2022 走看看