luoguP4175 CTSC2008网络管理

一句话题意:树上路径带修第k大

考虑树上不带修第k大怎么做的:
维护一个前缀主席树,然后\(u\), \(v\), \(LCA(u, v)\)和\(fa[LCA(u, v)]\)四个主席树相减即可

这题这么做就会出现一个问题
就是如果修改u的线段树的值,那么所有位于u的子树的线段树都要进行修改
如何高效修改子树内的线段树呢
想到了\(dfs\)序
一个子树在dfs序上面是一个连续的区间
那么如果我们把前缀主席树差分一下
(实际上就是变成了每个节点单独一个动态开点线段树)
然后子树修改就转化成了区间修改,区间修改又转化成了单点修改

那么如何查询呢
我们还是需要把u到根的路径上的线段树全部加起来
求前缀? 树状数组?
但是我们发现如果树状数组维护的是u到根的路径上的线段树,不好处理
只能类似倍增LCA的方法

可不可以有更好的方法?
我们发现,如果树状数组直接在dfs序上维护前缀会很好写
并且dfs序还有一个性质
就是对于u和他的一个祖先x
\(dfn[x]\) 至 \(dfn[u]\) 这一段就是x到u这条路径
令\(sum[x]\)表示dfs序上1至x的点的权值前缀和
那么\(sum[dfn[u]] - sum[dfn[u]]\)就是u到x这条链上的点权和
因为除了u到x的链以外的部分都相同,抵消了

所以这题也是同理
用树状数组维护一下dfs序的对应节点的线段树的前缀和即可

#include<bits/stdc++.h>

using namespace std;
const int MAXN = 200000 + 10;
const int INF = 0x3f3f3f3f;
#define lowbit(x) ((x) & (-(x)))
int head[MAXN], nxt[MAXN << 1], to[MAXN << 1], ind;
inline void add_edge(int u, int v){
    nxt[++ ind] = head[u]; head[u] = ind; to[ind] = v;
}
int n, q, a[MAXN], arr[MAXN], len;
struct Q{
    int k, a, b;
}query[MAXN];
inline void init(void){
    scanf("%d%d", &n, &q);
    int cnt = 0;
    for(register int i = 1; i <= n; ++ i)
        scanf("%d", &a[i]), arr[ ++ cnt] = a[i];
    int u, v;
    for(register int i = 1; i < n; ++ i){
        scanf("%d%d", &u, &v);
        add_edge(u, v); add_edge(v, u);
    }
    for(register int i = 1; i <= q; ++ i){
        scanf("%d%d%d", &query[i].k, &query[i].a, &query[i].b);
        if(query[i].k == 0) arr[++ cnt] = query[i].b;
    }
    sort(arr + 1, arr + cnt + 1);
    len = unique(arr + 1, arr + cnt + 1) - arr - 1;
}
int ls[MAXN * 400], rs[MAXN * 400], w[MAXN * 400], root[MAXN];
int fa[MAXN], son[MAXN], siz[MAXN], depth[MAXN], Top[MAXN], dfn[MAXN], dfs_clock;
int tot;
void modify(int &node, int l, int r, int pos, int val){
    if(!node) node = ++ tot;
    w[node] += val;
    if(l == r) return;
    int mid = (l + r) >> 1;
    if(pos <= mid) modify(ls[node], l, mid, pos, val);
    else modify(rs[node], mid + 1, r, pos, val);
}
void lowbitModify(int x, int pos, int val){
    for(register int i = x; i <= n; i += lowbit(i))
        modify(root[i], 1, len, pos, val);
}
void dfs1(int node, int fath){
    dfn[node] = ++ dfs_clock;
    fa[node] = fath; siz[node] = 1; depth[node] = depth[fath] + 1;
    for(register int i = head[node]; i; i = nxt[i]){
        int j = to[i]; if(j == fath) continue;
        dfs1(j, node);
        siz[node] += siz[j];
        if(siz[son[node]] < siz[j]) son[node] = j;
    }
}
void dfs2(int node, int topn){
    Top[node] = topn;
    if(son[node]) dfs2(son[node], topn);
    for(register int i = head[node]; i; i = nxt[i]){
        int j = to[i]; if(j == son[node] || j == fa[node]) continue;
        dfs2(j, j);
    }
}
inline int LCA(int u, int v){
    while(Top[u] != Top[v]){
        if(depth[Top[u]] < depth[Top[v]]) swap(u, v);
        u = fa[Top[u]];
    }
    if(depth[u] > depth[v]) swap(u, v);
    return u;
}
int tmp[2][MAXN];
int Query(int l, int r, int k){
    if(l == r) return l;
    int mid = (l + r) >> 1;
    int sum = 0;
    for(register int i = 1; i <= tmp[1][0]; ++ i) sum += w[rs[tmp[1][i]]];
    for(register int i = 1; i <= tmp[0][0]; ++ i) sum -= w[rs[tmp[0][i]]];
    if(k <= sum){
        for(register int i = 1; i <= tmp[1][0]; ++ i) tmp[1][i] = rs[tmp[1][i]];
        for(register int i = 1; i <= tmp[0][0]; ++ i) tmp[0][i] = rs[tmp[0][i]];
        return Query(mid + 1, r, k);
    }
    else{
        for(register int i = 1; i <= tmp[1][0]; ++ i) tmp[1][i] = ls[tmp[1][i]];
        for(register int i = 1; i <= tmp[0][0]; ++ i) tmp[0][i] = ls[tmp[0][i]];
        return Query(l, mid, k - sum);
    }
}
inline int lowbitQuery(int u, int v, int k){
    tmp[0][0] = tmp[1][0] = 0;
    int lca = LCA(u, v);
    if(depth[u] + depth[v] - depth[lca] - depth[fa[lca]] < k) return -INF;
    for(register int i = dfn[u]; i; i -= lowbit(i)) tmp[1][++ tmp[1][0]] = root[i];
    for(register int i = dfn[v]; i; i -= lowbit(i)) tmp[1][++ tmp[1][0]] = root[i];
    for(register int i = dfn[lca]; i; i -= lowbit(i)) tmp[0][++ tmp[0][0]] = root[i];
    for(register int i = dfn[fa[lca]]; i; i -= lowbit(i)) tmp[0][++ tmp[0][0]] = root[i];
    return Query(1, len, k);
}
inline void work(void){
    dfs1(1, 0); dfs2(1, 1);
    for(register int i = 1; i <= n; ++ i) a[i] = lower_bound(arr + 1, arr + len + 1, a[i]) - arr;
    for(register int i = 1; i <= q; ++ i){
        if(query[i].k == 0) query[i].b = lower_bound(arr + 1, arr + len + 1, query[i].b) - arr;
    }
    for(register int i = 1; i <= n; ++ i){
        lowbitModify(dfn[i], a[i], 1); lowbitModify(dfn[i] + siz[i], a[i], -1);
    }
    for(register int i = 1; i <= q; ++ i){
        //错误笔记:把q写成n导致TLE
        if(query[i].k == 0){
            int p = query[i].a;
            lowbitModify(dfn[p], a[p], -1); lowbitModify(dfn[p] + siz[p], a[p], 1);
            a[p] = query[i].b;
            lowbitModify(dfn[p], a[p], 1); lowbitModify(dfn[p] + siz[p], a[p], -1);
        }
        else {
            int ans = lowbitQuery(query[i].a, query[i].b, query[i].k);
            if(ans == -INF) printf("invalid request!\n");
            else printf("%d\n", arr[ans]);
        }
    }
}

int main(){
    init(); work();
    return 0;
}

原文地址:https://www.cnblogs.com/FlySakura/p/12236426.html

时间: 2024-10-11 05:44:27

luoguP4175 CTSC2008网络管理的相关文章

BZOJ 1146: [CTSC2008]网络管理Network [树上带修改主席树]

1146: [CTSC2008]网络管理Network Time Limit: 50 Sec  Memory Limit: 162 MBSubmit: 3522  Solved: 1041[Submit][Status][Discuss] Description M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个 部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成. 每个部门都有一个专属的路由器,

BZOJ 1146: [CTSC2008]网络管理Network( 树链剖分 + 树状数组套主席树 )

树链剖分完就成了一道主席树裸题了, 每次树链剖分找出相应区间然后用BIT+(可持久化)权值线段树就可以完成计数. 但是空间问题很严重....在修改时不必要的就不要新建, 直接修改原来的..详见代码. 时间复杂度O(N*log^3(N)) ---------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

【BZOJ 1146】 [CTSC2008]网络管理Network

1146: [CTSC2008]网络管理Network Time Limit: 50 Sec  Memory Limit: 162 MB Submit: 1938  Solved: 577 [Submit][Status] Description M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机

BZOJ 1146: [CTSC2008]网络管理Network

1146: [CTSC2008]网络管理Network Time Limit: 50 Sec  Memory Limit: 162 MBSubmit: 3539  Solved: 1054[Submit][Status][Discuss] Description M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个 部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成. 每个部门都有一个专属的路由器,

BZOJ_1146_[CTSC2008]网络管理Network_主席树+树状数组

BZOJ_1146_[CTSC2008]网络管理Network_主席树 Description M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个 部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成. 每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部 门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些

bzoj1146 [CTSC2008]网络管理Network

Description M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由

CODEVS1490 [CTSC2008]网络管理

题目描述 Description M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间 协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有 机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略