1146: [CTSC2008]网络管理Network 树套树,二分,树剖

[Submit][Status][Discuss]
Description
  M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门。为了让分布在世界各地的N个
部门之间协同工作,公司搭建了一个连接整个公司的通信网络。该网络的结构由N个路由器和N-1条高速光缆组成。
每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部
门进行通信联络。该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信。 高速光
缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略。但是由于路由器老化,在这些路由器上进行
数据交换会带来很大的延迟。而两个路由器之间的通信延迟时间则与这两个路由器通信路径上所有路由器中最大的
交换延迟时间有关。作为M公司网络部门的一名实习员工,现在要求你编写一个简单的程序来监视公司的网络状况
。该程序能够随时更新网络状况的变化信息(路由器数据交换延迟时间的变化),并且根据询问给出两个路由器通
信路径上延迟第k大的路由器的延迟时间。【任务】 你的程序从输入文件中读入N个路由器和N-1条光缆的连接信息
,每个路由器初始的数据交换延迟时间Ti,以及Q条询问(或状态改变)的信息。并依次处理这Q条询问信息,它们
可能是: 1. 由于更新了设备,或者设备出现新的故障,使得某个路由器的数据交换延迟时间发生了变化。 2. 查
询某两个路由器a和b之间的路径上延迟第k大的路由器的延迟时间。
Input
  第一行为两个整数N和Q,分别表示路由器总数和询问的总数。第二行有N个整数,第i个数表示编号为i的路由
器初始的数据延迟时间Ti。紧接着N-1行,每行包含两个整数x和y。表示有一条光缆连接路由器x和路由器y。紧接
着是Q行,每行三个整数k、a、b。如果k=0,则表示路由器a的状态发生了变化,它的数据交换延迟时间由Ta变为b
。如果k>0,则表示询问a到b的路径上所经过的所有路由器(包括a和b)中延迟第k大的路由器的延迟时间。注意N
,Q<=80000,任意一个路由器在任何时刻都满足延迟时间小于10^8。对于所有询问满足0<=K<=N
Output
  对于每一个第二种询问(k>0),输出一行。包含一个整数为相应的延迟时间。如果路径上的路由器不足k个,
则输出信息“invalid request!”(全部小写不包含引号,两个单词之间有一个空格)。
Sample Input
5 5

5 1 2 3 4

3 1

2 1

4 3

5 3

2 4 5

0 1 2

2 2 3

2 1 4

3 3 5

Sample Output
3

2

2

invalid request!

解题方法: 我真是*, 写了一下午加一晚上的树套树,代码330排,期间各种TLE+WA,各种小错误啊,自己真是太菜了啊,长代码一直雪崩啊。。。树套树的思路还是非常简单的, 首先将树按链重新标号,然后将一段链给放到线段树中取。由于求u到v的k大值,那么我们可以对于一段链中二分查找对于某一个值小于这个值的个数有多少个,那么我们就可以知道这个值如果恰好是第k大的就是答案所求。用线段树的话可以将一段区间的值快速求出来
实现就是对于线段树表示表示区间的节点,对于那一段区间我们建立一个平衡树(我用的multitreap,用于统计大于某个值的个数有多少个),然后对于每一个二分答案,我们对于这个值在u到v的树链上求小于这个值的答案有多少个就好了,直接按树链剖分的方法往一端靠上去求累计和就好了。
其总的复杂度是n(logn)^4其实想想这个复杂度对于一组数据已经接近n^2了但是考虑到线段树的区间更新,我们其实区间的查询能大大提高效率,20s可以过。
由于我的模板太过模式化了,写出来调得没编译错误都花了快10分钟!

#include <bits/stdc++.h>
using namespace std;
const int maxn = 400010;
const int maxv = 17;
int n, q, edgecnt, tot, tmp;
int T[maxn], Hash[maxn], f[maxn], a[maxn], b[maxn];
bool vis[maxn];
//===============================================================multitreap
namespace multi_treap{
    struct data{
        int l, r, v, size, rnd, w;
    }tree[5000010];
    int size, root, ans;
    void update(int k)
    {
        tree[k].size = tree[tree[k].l].size + tree[tree[k].r].size + tree[k].w;
    }
    void rturn(int &k)
    {
        int t = tree[k].l;
        tree[k].l = tree[t].r;
        tree[t].r = k;
        tree[t].size = tree[k].size;
        update(k);
        k = t;
    }
    void lturn(int &k)
    {
        int t = tree[k].r;
        tree[k].r = tree[t].l;
        tree[t].l = k;
        tree[t].size = tree[k].size;
        update(k);
        k=t;
    }
    void insert(int &k, int x)
    {
        if(k == 0)
        {
            size++;
            k = size;
            tree[k].size = tree[k].w = 1;
            tree[k].v = x;
            tree[k].rnd = rand();
            return ;
        }
        tree[k].size++;
        if(tree[k].v==x) tree[k].w++;//每个结点顺便记录下与该节点相同值的数的个数
        else if(x > tree[k].v)
        {
            insert(tree[k].r, x);
            if(tree[tree[k].r].rnd < tree[k].rnd) lturn(k);//维护堆性质
        }
        else
        {
            insert(tree[k].l, x);
            if(tree[tree[k].l].rnd < tree[k].rnd) rturn(k);
        }
    }
    void del(int &k,int x)
    {
        if(k == 0)return;
        if(tree[k].v == x)
        {
            if(tree[k].w > 1)
            {
                tree[k].w--;
                tree[k].size--;
                return;//若不止相同值的个数有多个,删去一个
            }
            if(tree[k].l * tree[k].r == 0)k = tree[k].l + tree[k].r;//有一个儿子为空
            else if(tree[tree[k].l].rnd < tree[tree[k].r].rnd)
                rturn(k),del(k,x);
            else lturn(k),del(k,x);
        }
        else if(x > tree[k].v)
            tree[k].size--, del(tree[k].r,x);
        else tree[k].size--, del(tree[k].l,x);
    }
//    int query_rank(int k, int x)
//    {
//        if(k == 0) return 0;
//        if(tree[k].v == x) return tree[tree[k].l].size + 1;
//        else if(x > tree[k].v)
//            return tree[tree[k].l].size + tree[k].w + query_rank(tree[k].r, x);
//        else return query_rank(tree[k].l, x);
//    }
    void query_rank(int k, int x){
        if(!k) return ;
        if(x == tree[k].v){
            tmp += tree[tree[k].r].size;
            return ;
        }
        else if(x < tree[k].v){
            tmp += tree[tree[k].r].size + tree[k].w;
            query_rank(tree[k].l, x);
        }
        else query_rank(tree[k].r, x);
    }
    int query_num(int k, int x)
    {
        if(k == 0) return 0;
        if(x <= tree[tree[k].l].size)
            return query_num(tree[k].l, x);
        else if(x > tree[tree[k].l].size + tree[k].w)
            return query_num(tree[k].r,x - tree[tree[k].l].size-tree[k].w);
        else return tree[k].v;
    }
//    void query_pro(int k, int x)
//    {
//        if(k == 0)return;
//        if(tree[k].v < x)
//        {
//            ans = k;
//            query_pro(tree[k].r, x);
//        }
//        else query_pro(tree[k].l, x);
//    }
//    void query_sub(int k, int x)
//    {
//        if(k == 0)return;
//        if(tree[k].v > x)
//        {
//            ans = k;
//            query_sub(tree[k].l, x);
//        }
//        else query_sub(tree[k].r, x);
//    }
}
//==================================================================================graph
int head[maxn];
struct edge{
    int v, nxt;
    edge(){}
    edge(int v, int nxt) : v(v), nxt(nxt) {}
}E[200005];
void init(){
    memset(head, -1, sizeof(head));
    edgecnt = 0;
}
void ins(int u, int v){
    E[edgecnt].v  = v, E[edgecnt].nxt = head[u], head[u] = edgecnt++;
}
//===================================================================================
int find(int x){
    int l = 1, r = tot;
    while(l <= r){
        int mid = (l + r) / 2;
        if(Hash[mid] < x) l = mid + 1;
        else if(Hash[mid] == x) return mid;
        else r = mid - 1;
    }
    return l;
}
//====================================================================================
namespace shupou{
    int dfn, fa[maxn][maxv], son[maxn], dep[maxn], belong[maxn], pl[maxn];
    void dfs1(int x){
        son[x] = 1; vis[x] = 1;
        for(int i = 1; i <= 16; i++){
            if((1 << i) <= dep[x]) fa[x][i] = fa[fa[x][i-1]][i-1];
            else break;
        }
        for(int i = head[x]; ~i; i = E[i].nxt){
            int v = E[i].v;
            if(vis[v]) continue;
            dep[v] = dep[x] + 1;
            fa[v][0] = x;
            dfs1(v);
            son[x] += son[v];
        }
    }
    void dfs2(int x, int chain){
        dfn++;
        pl[x] = dfn;//分配x节点在线段树中的编号
        belong[x] = chain;//记录链的顶端
        int k = 0;
        for(int i = head[x]; ~i; i = E[i].nxt){
            int v = E[i].v;
            if(son[v] > son[k] && dep[v] > dep[x]){
                k = v; //选择子树最大的儿子继承重链
            }
        }
        if(!k) return ;
        dfs2(k, chain);
        for(int i = head[x]; ~i; i = E[i].nxt){
            int v = E[i].v;
            if(v != k && dep[v] > dep[x]){
                dfs2(v, v);
            }
        }
    }
    int lca(int x, int y){
        if(dep[x] < dep[y]) swap(x, y);
        for(int i = 0; i <= 16; i++){
            if((dep[x] - dep[y]) & (1<<i)) x = fa[x][i];
        }
        for(int i = 16; i >= 0; i--){
            if(fa[x][i] != fa[y][i]){
                x = fa[x][i]; y = fa[y][i];
            }
        }
        if(x == y) return x;
        return fa[x][0];
    }
}
//====================================================================================segment tree
namespace segmenttree{
    int root[300005];
    void Build(){
        memset(root, 0, sizeof(root));
    }
    void update(int pos, int x, int y, int l, int r, int o){
        multi_treap::del(root[o], x);
        multi_treap::insert(root[o], y);
        if(l == r) return ;
        int mid = (l + r) / 2;
        if(pos <= mid) update(pos, x, y, l, mid, o*2);
        else update(pos, x, y, mid + 1, r, o*2+1);
    }
    void query(int x, int y, int num, int l, int r, int o){
        if(x == l && y == r) {
            multi_treap::query_rank(root[o], num);
            return ;
        }
        int mid = (l + r) / 2;
        if(y <= mid) query(x, y, num, l, mid, o*2);
        else if(x > mid) query(x, y, num, mid + 1, r, o*2+1);
        else{
            query(x, mid, num, l, mid, o*2);
            query(mid + 1, y, num, mid + 1, r, o*2 + 1);
        }
    }
}
//=======================================================================================
void get_rank(int x, int f, int num){
    while(shupou::belong[x] != shupou::belong[f]){
        segmenttree::query(shupou::pl[shupou::belong[x]], shupou::pl[x], num, 1, n, 1);
        x = shupou::fa[shupou::belong[x]][0];
    }
    segmenttree::query(shupou::pl[f], shupou::pl[x], num, 1, n, 1);
}
//=======================================================================================
void work(int x, int y, int rank){
    int _lca = shupou::lca(x, y);
    int pos = -1;
    tmp = 0;
    get_rank(y, _lca, 0);
    get_rank(x, _lca, 0);
    if(tmp - 1 < rank){
        puts("invalid request!");
        return ;
    }
    int l = 1, r = tot;
    while(l <= r){
        int mid = (l + r) / 2;
        tmp = 0;
        get_rank(x, _lca, mid);
        get_rank(y, _lca, mid);
        if(T[_lca] > mid) tmp--;
        if(tmp <= rank - 1){
            r = mid - 1;
            pos = mid;
        }
        else{
            l = mid + 1;
        }
    }
    printf("%d\n", Hash[pos]);
}
int main(){
    using namespace multi_treap;
    using namespace shupou;
    using namespace segmenttree;
    init();
    scanf("%d", &n);
    scanf("%d", &q);
    for(int i = 1; i <= n; i++){
        scanf("%d", &T[i]);
        Hash[++tot] = T[i];
    }
    for(int i = 1; i < n; i++){
        int u, v;
        scanf("%d%d", &u, &v);
        ins(u, v);
        ins(v, u);
    }
    dfs1(1);
    dfs2(1, 1);
    for(int i = 1; i <= q; i++){
        scanf("%d%d%d", &f[i], &a[i], &b[i]);
        if(!f[i]) Hash[++tot] = b[i];
    }
    sort(Hash + 1, Hash + 1 + tot);
    int top = 1;
    for(int i = 2; i <= tot; i++){
        if(Hash[i] != Hash[i-1]){
            Hash[++top] = Hash[i];
        }
    }
    tot = top;
    for(int i = 1; i <= n; i++) T[i] = find(T[i]);
    for(int i = 1; i <= q; i++){
        if(!f[i]){
            b[i] = find(b[i]);
        }
    }
    for(int i = 1; i <= n; i++){
        segmenttree::update(pl[i], 0, T[i], 1, n, 1);
    }
    for(int i = 1; i <= q; i++){
        if(!f[i]){
            segmenttree::update(pl[a[i]], T[a[i]], b[i], 1, n, 1);
            T[a[i]] = b[i];
        }
        else{
            work(a[i], b[i], f[i]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值