Luogu 3242 [HNOI2015]接水果

BZOJ4009 权限题

真的不想再写一遍了

大佬blog

假设有果实$(x, y)$,询问$(a, b)$,用$st_i$表示$i$的$dfs$序,用$ed_i$表示所有$i$的子树搜完的$dfs$序,那么果实对询问产生贡献只会有两种情况:

1、这个果实表示的区间是一条链

  不妨假设$dep_x < dep_y$,记$z$为$x$到$y$的树链上的从$x$向下走的第一个点,画个图可以发现$(a, b)$需要满足:

    $st_a \in [1, st_z - 1] \cup [ed_z + 1, n], \ st_b \in [st_y, ed_y]$,其中$(a, b)$可以互换。

2、这个果实表示的链是一条先向上再向下的纯正的树链

  仍然画个图发现$(a, b)$需要满足:$st_a \in [st_x, ed_x], \ st_b \in [st_y, ed_y]$, $(a, b)$仍然可以互换。

对于每一个询问$(a, b)$,只要看一看有多少果实满足上面两种选一种的条件,然后求个$k$小就好了。

发现这其实是一个在二维矩阵中动态加点求$k$小的问题,这时候$KDTree$就出现了,然而我不会……

考虑扫描线降维,一个矩阵根据$x$坐标拆成两条线然后排个序扫一扫,每一次根据询问的点的位置进行加入删除的调整然后求个$k$小就好了。

也就是说只要写一个支持在一条线上区间加区间减然后求$k$小的数据结构就好了,我们需要一个外层权值内层下标的线段树兹磁这个操作。

瓶颈在于树套树的$O(nlog^2n)$。

最好标记永久化一下。

Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 4e4 + 5;
const int M = 3e7 + 5;
const int Lg = 18;

int n, m, qn, lcnt = 0, mx = 0, num[N], tot = 0, head[N];
int dfsc = 0, st[N], ed[N], fa[N][Lg], dep[N], ans[N];

struct Edge {
    int to, nxt;
} e[N << 1];

inline void add(int from, int to) {
    e[++tot].to = to;
    e[tot].nxt = head[from];
    head[from] = tot;
}

struct Line {
    int pos, x, y, v, type;
    
    inline Line (int nowPos = 0, int nowX = 0, int nowY = 0, int nowV = 0, int nowType = 0) {
        pos = nowPos, x = nowX, y = nowY, v = nowV, type = nowType;
    }
    
    friend bool operator < (const Line &u, const Line &v) {
        return u.pos < v.pos;
    }
    
} a[N << 3];

inline void addLine(int l1, int r1, int l2, int r2, int v) {
    a[++lcnt] = Line(l1, l2, r2, v, 1);
    a[++lcnt] = Line(r1 + 1, l2, r2, v, -1);
}

struct Querys {
    int x, y, k, id;
    
    friend bool operator < (const Querys &u, const Querys &v) {
        return u.x < v.x;    
    }
    
} q[N];

inline void read(int &X) {
    X = 0; char ch = 0; int op = 1;
    for(; ch > '9' || ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

inline void swap(int &x, int &y) {
    int t = x; x = y; y = t;
}

void dfs(int x, int fat, int depth) {
    fa[x][0] = fat, dep[x] = depth, st[x] = ++dfsc;
    for(int i = 1; i <= 16; i++)
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
    for(int i = head[x]; i; i = e[i].nxt) {
        int y = e[i].to;
        if(y == fat) continue;
        dfs(y, x, depth + 1);
    }
    ed[x] = dfsc;
}

inline int getPos(int x, int stp) {
    int res = x;
    for(int i = 16; i >= 0; i--)
        if((stp >> i) & 1) res = fa[res][i];
    return res;
}

namespace InSegT {
    struct Node {
        int lc, rc, sum;
    } s[M];
    
    int nodeCnt = 0;
    
    #define lc s[p].lc
    #define rc s[p].rc
    #define sum(p) s[p].sum
    #define mid ((l + r) >> 1)
    
    void modify(int &p, int l, int r, int x, int y, int v) {
        if(!p) p = ++nodeCnt;
        
        if(x <= l && y >= r) {
            sum(p) += v;
            return;
        }
        
        if(x <= mid) modify(lc, l, mid, x, y, v);
        if(y > mid) modify(rc, mid + 1, r, x, y, v);
    }
    
    int query(int p, int l, int r, int x) {
        if(l == r) return sum(p);
        int res = sum(p);
        if(x <= mid) res += query(lc, l, mid, x);
        else res += query(rc, mid + 1, r, x);
        return res;
    }
    
    #undef lc
    #undef rc
    #undef sum
}

namespace OutSegT {
    using namespace InSegT;
    
    #define lc p << 1
    #define rc p << 1 | 1 
    
    int root[N << 2];
    
    void ins(int p, int l, int r, int x, int y, int pos, int type) {
        modify(root[p], 1, n, x, y, type);
        if(l == r) return;
        
        if(pos <= mid) ins(lc, l, mid, x, y, pos, type);
        else ins(rc, mid + 1, r, x, y, pos, type);
    }
    
    int getKth(int p, int l, int r, int x, int k) {
        if(l == r) return l;
        int now = query(root[lc], 1, n, x);
        if(k <= now) return getKth(lc, l, mid, x, k);
        else return getKth(rc, mid + 1, r, x, k - now);
    }
    
} using namespace OutSegT;

int main() {
    read(n), read(m), read(qn);
    for(int x, y, i = 1; i < n; i++) {
        read(x), read(y);
        add(x, y), add(y, x);
    }
    dfs(1, 0, 1);
    
    for(int x, y, v, i = 1; i <= m; i++) {
        read(x), read(y), read(v);
        num[++mx] = v;
        if(dep[x] > dep[y]) swap(x, y);
        if(st[x] <= st[y] && ed[x] >= ed[y]) {
            int z = getPos(y, dep[y] - dep[x] - 1);
            if(ed[z] < n) {
                addLine(st[y], ed[y], ed[z], n, v);
                addLine(ed[z], n, st[y], ed[y], v);            
            }
            if(st[z] > 1) {
                addLine(st[y], ed[y], 1, st[z] - 1, v);
                addLine(1, st[z] - 1, st[y], ed[y], v);    
            }
        } else {
            addLine(st[x], ed[x], st[y], ed[y], v);
            addLine(st[y], ed[y], st[x], ed[x], v);
        }
    }
    
    sort(num + 1, num + mx + 1);
    mx = unique(num + 1, num + 1 + mx) - num - 1;
    for(int i = 1; i <= lcnt; i++)
        a[i].v = lower_bound(num + 1, num + mx + 1, a[i].v) - num;
    
    for(int i = 1; i <= qn; i++) {
        read(q[i].x), read(q[i].y), read(q[i].k);
        q[i].x = st[q[i].x], q[i].y = st[q[i].y];
        q[i].id = i;
    }    
    
    sort(q + 1, q + 1 + qn);
    sort(a + 1, a + 1 + lcnt);
    for(int j = 1, i = 1; i <= qn; i++) {
        for(; j <= lcnt && a[j].pos <= q[i].x; ++j)
            ins(1, 1, mx, a[j].x, a[j].y, a[j].v, a[j].type);
        ans[q[i].id] = getKth(1, 1, mx, q[i].y, q[i].k);
    }
    
    for(int i = 1; i <= qn; i++)
        printf("%d\n", num[ans[i]]);
    
    return 0;
}
View Code

 

posted @ 2018-11-06 21:04  CzxingcHen  阅读(197)  评论(1编辑  收藏  举报