bzoj 1146 网络管理Network (CDQ 整体二分 + 树刨)

题目传送门

题意:求树上路径可修改的第k大值是多少。

题解:CDQ整体二分+树刨。

每一个位置上的数都会有一段持续区间

根据CDQ拆的思维,可以将这个数拆成出现的时间点和消失的时间点。

然后通过整体二分第k大思路 + 树炮询问路径上出现点的个数就好了。

说一下整体二分的思路。

先假设第k大的值是mid, 然后按照时间顺序,出现一个数<=mid标记这个数的位置为1, 消失一个数<=mid,标记这个数的位置为0。

然后对于询问来说,询问路径上的值, 与 k进行比较, 如果 值 >= k则说明这个询问的第k大落在区间[ l, mid]之间, 否则落在 [mid+1,r]之间,并且第k大是在[mid+1,r] 的 k -= 路劲值。

然后将所有 修改且值 <= mid 和 询问第k大落在左边的 询问放到 数组的左边, 其他放到数组的右边, 然后递归下去处理左边 / 右边的这个区间。

这样每个操作最多只会访问lg次。

每次操作的复杂度是lg*lg。

所以最后的复杂度是n*lg^3.

代码:

#include<bits/stdc++.h>
using namespace std;
#define Fop freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout);
#define LL long long
#define ULL unsigned LL
#define fi first
#define se second
#define pb push_back
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lch(x) tr[x].son[0]
#define rch(x) tr[x].son[1]
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))
typedef pair<int,int> pll;
const int inf = 0x3f3f3f3f;
const int _inf = 0xc0c0c0c0;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL _INF = 0xc0c0c0c0c0c0c0c0;
const LL mod =  (int)1e9+7;
const int N = 8e5 + 100;
const int M = 2 * N;
int head[N], to[M], nt[M];
int sz[N], son[N], deep[N], top[N], fa[N], dfn[N], dto[N], tr[N<<2];
int tot, dtot;
int n;
int a[N];
struct Node{
    int k, a, b, id;
}A[M], B[M], C[M];
/**
k == -1 Add -> pos[a] = b
k == -2 Del -> pos[a] = 0
else k Query with ans[id]
**/
void add(int u, int v){
    to[tot] = v; nt[tot] = head[u]; head[u] = tot++;

    to[tot] = u; nt[tot] = head[v]; head[v] = tot++;
}
void dfs1(int o, int u){
    sz[u] = 1;
    for(int i = head[u]; ~i; i = nt[i]){
        int v = to[i];
        if(v == o) continue;
        dfs1(u, v);
        if(sz[v] > sz[son[u]]) son[u] = v;
        sz[u] += sz[v];
    }
}
void dfs2(int o, int u, int t){
    deep[u] = deep[o] + 1;
    top[u] = t;
    fa[u] = o;
    dfn[u] = ++dtot;
    dto[dtot] = u;
    if(son[u]) dfs2(u, son[u], t);
    for(int i = head[u]; ~i; i = nt[i]){
        int v = to[i];
        if(v == o || v == son[u]) continue;
        dfs2(u, v, v);
    }
}
void PushUp(int rt){
    tr[rt] = tr[rt<<1] + tr[rt<<1|1];
}
int Query(int L, int R, int l, int r, int rt){
    if(L <= l && r <= R)
        return tr[rt];
    int m = l+r >> 1;
    int ret = 0;
    if(L <= m) ret += Query(L, R, lson);
    if(m < R) ret += Query(L, R, rson);
    return ret;
}
void Updata(int L, int C, int l, int r, int rt){
    if(l == r){
        tr[rt] = C;
        return ;
    }
    int m = l+r >> 1;
    if(L <= m) Updata(L, C, lson);
    else Updata(L, C, rson);
    PushUp(rt);
    return ;
}
int Query_Path(int x, int y){
    int fx = top[x], fy = top[y];
    int ret = 0;
    while(fx != fy){
        if(deep[fx] > deep[fy]){
            ret += Query(dfn[fx],dfn[x],1,n,1);
            x = fa[fx]; fx = top[x];
        }
        else {
            ret += Query(dfn[fy],dfn[y],1,n,1);
            y = fa[fy]; fy = top[y];
        }
    }
    if(deep[x] < deep[y]) ret += Query(dfn[x], dfn[y], 1, n, 1);
    else ret += Query(dfn[y], dfn[x], 1, n,1);
    return ret;
}
int lca(int x, int y){
    int fx = top[x], fy = top[y];
    while(fx != fy){
        if(deep[fx] > deep[fy])
            x = fa[fx]; fx = top[x];
        else
            y = fa[fy]; fy = top[y];
    }
    if(deep[x] < deep[y]) return x;
    return y;
}
void init(){
    memset(head, -1, sizeof(head));
    memset(son, 0, sizeof son);
    tot = dtot = 0;
}
int atot = 0;
void AddNode(int op, int a, int b, int id){
    A[++atot] = {op, a, b, id};
}
int ans[N];
void cdq(int ansl, int ansr, int l, int r){
    if(l > r) return ;
    if(ansl == ansr){
        for(int i = l; i <= r; ++i)
            if(A[i].id)
                ans[A[i].id] = ansl;
        return ;
    }
    int mid = ansl+ansr >> 1;
    int tb = 0, tc = 0;
    for(int i = l; i <= r; ++i){
        if(A[i].k == -1){
            if(A[i].b <= mid){
                B[++tb] = A[i];
                Updata(dfn[A[i].a], 1, 1, n, 1);
            }
            else {
                C[++tc] = A[i];
            }
        }
        else if(A[i].k == -2){
            if(A[i].b <= mid){
                B[++tb] = A[i];
                Updata(dfn[A[i].a], 0, 1, n, 1);
            }
            else
                C[++tc] = A[i];
        }
        else {
            int kk = Query_Path(A[i].a, A[i].b);
            if(kk >= A[i].k)
                B[++tb] = A[i];
            else
                A[i].k -= kk;
                C[++tc] = A[i];
        }
    }
    for(int i = 1; i <= tb; ++i)
        A[i+l-1] = B[i];
    for(int i = 1; i <= tc; ++i)
        A[tb+l+i-1] = C[i];
    cdq(ansl, mid, l, tb+l-1);
    cdq(mid+1, ansr, tb+l, r);
}
int main(){
    init();
    int q;
    scanf("%d%d", &n, &q);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        AddNode(-1, i, a[i], 0);
    }
    int x, y;
    for(int i = 1; i < n; ++i) {
        scanf("%d%d", &x, &y);
        add(x, y);
    }
    int m = 0;
    dfs1(1,1);
    dfs2(1,1,1);
    int k;
    for(int i = 1; i <= q; ++i){
        scanf("%d%d%d", &k, &x, &y);
        if(k) {
            int z = lca(x,y);
            z = deep[x] + deep[y] - deep[z] * 2 + 1;
            if(z < k){
                ans[++m] = -1;
            }
            else AddNode(z-k+1, x, y, ++m);
        }
        else {
            AddNode(-2, x, a[x], 0);
            AddNode(-1, x, y, 0);
            a[x] = y;
        }
    }
    for(int i = 1; i <= n; ++i)
        AddNode(-2, i, a[i], 0)
    cdq(1, 1e8+10, 1, atot);
    for(int i = 1; i <= m; ++i)
        if(~ans[i]) printf("%d\n", ans[i]);
        else puts("invalid request!");
    return 0;
}

原文地址:https://www.cnblogs.com/MingSD/p/10805687.html

时间: 2024-08-10 21:35:26

bzoj 1146 网络管理Network (CDQ 整体二分 + 树刨)的相关文章

[BZOJ 1146]网络管理Network 树上带修改路径k值

题目意思非常清楚,就是要求树上带修改的路径k大值 如果不带修改的话,我会用树上主席树去搞,以父子关系建树,可以参见 [BZOJ 3123]森林 但是带修改就不会打了QAQ,于是去学了另一种在dfs序上搞的方法(同时感谢呵呵酵母菌的帮助) 其实思想是一样的,就是搞出来节点到根路径的线段树,然后用x+y-lca-fa(lca)去差分出来树上路径的线段树,再去里面查询k值 那么我们怎么得到点到根路径的线段树呢?可以在dfs序上差分啊! dfs序列上的dfnl[x]~dfnr[x]包含的是以x为根节点的

[CTSC2008]网络管理(整体二分+树剖+树状数组)

一道经典的带修改树链第 \(k\) 大的问题. 我只想出三个 \(\log\) 的解法... 整体二分+树剖+树状数组. 那不是暴力随便踩的吗??? 不过跑得挺快的. \(Code\ Below:\) // luogu-judger-enable-o2 #include <bits/stdc++.h> #define lowbit(x) ((x)&(-(x))) using namespace std; const int maxn=80000+10; const int lim=1e

【bzoj2527】[Poi2011]Meteors 整体二分+树状数组

题目描述 有N个成员国.现在它发现了一颗新的星球,这颗星球的轨道被分为M份(第M份和第1份相邻),第i份上有第Ai个国家的太空站. 这个星球经常会下陨石雨.BIU已经预测了接下来K场陨石雨的情况.BIU的第i个成员国希望能够收集Pi单位的陨石样本.你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石. 输入 第一行是两个数N,M. 第二行有M个数,第i个数Oi表示第i段轨道上有第Oi个国家的太空站. 第三行有N个数,第i个数Pi表示第i个国家希望收集的陨石数量. 第四行有一个

【BZOJ3110】【整体二分+树状数组区间修改/线段树】K大数查询

Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M 接下来M行,每行形如1 a b c或2 a b c Output 输出每个询问的结果 Sample Input 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3 Sample Output 1 2 1 HINT

【BZOJ-2527】Meteors 整体二分 + 树状数组

2527: [Poi2011]Meteors Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 831  Solved: 306[Submit][Status][Discuss] Description Byteotian Interstellar Union (BIU) has recently discovered a new planet in a nearby galaxy. The planet is unsuitable for colo

【bzoj3110】[Zjoi2013]K大数查询 整体二分+树状数组区间修改

题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. 输入 第一行N,M接下来M行,每行形如1 a b c或2 a b c 输出 输出每个询问的结果 样例输入 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3 样例输出 1 2 1 题解 整体二分+树状数组区间修改 当年naive的树套树题解 前两天由于要

bzoj 2527 Meteors - 整体二分 - 树状数组

Description Byteotian Interstellar Union (BIU) has recently discovered a new planet in a nearby galaxy. The planet is unsuitable for colonisation due to strange meteor showers, which on the other hand make it an exceptionally interesting object of st

BZOJ 2738 矩阵乘法(整体二分+二维树状数组)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2738 [题目大意] 给出一个方格图,询问要求求出矩阵内第k小的元素 [题解] 我们对答案的大小进行整体二分,用二维树状数组维护二维区间和, 将超过数量的分治到左区间,不满足的分治到右区间即可. [代码] #include <cstdio> #include <algorithm> #include <cstring> using namespace std;

bzoj1146整体二分+树链剖分+树状数组

其实也没啥好说的 用树状数组可以O(logn)的查询 套一层整体二分就可以做到O(nlngn) 最后用树链剖分让序列上树 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 inline int read() 7 { 8 int x=0,f=1,ch=getchar(); 9 while(ch<