【CTSC 2008】 网络管理 --树链剖分+树状数组+Trie树

【题目大意】

给定一颗点上带权的树,每次可以修改某点的权值,或者询问两点u,v树上唯一路径的第k大权值。


【分析】

这类树上路径问题直接轻重权树链剖分就好啦。。

但是是询问第k大,所以我们用一个树状数组套Trie树维护区间权值即可

。。

注意用BFS来剖分避免爆栈


【代码】

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <deque>
#include <vector>

using namespace std;

#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define rrep(i,b,a) for(int i=(b);i>=(a);--i)
#define MAXN 80010
#define lowbit(x) ((x)&(-(x)))
#define sf scanf
#define pf printf
#define p_b push_back
#define p_f push_front
#define pp_f pop_front

string error("invalid request!");
struct edge{
    int v;
    edge* next;
}*head[MAXN],Edge[MAXN<<1];
struct TrieNode{
    #define node TrieNode
    node* ch[2];
    int sz;
}*root[MAXN],pool[MAXN*200],*New=pool;
int N,Q;
int tid[MAXN],top[MAXN];
int size[MAXN],son[MAXN];
int fa[MAXN],dep[MAXN];
int val[MAXN];

int totEdge;
void Addedge(int u,int v){
    Edge[totEdge].v=v;
    Edge[totEdge].next=head[u];
    head[u]=&Edge[totEdge++];
}

node* Get(){
    New->ch[0]=New->ch[1]=NULL;
    New->sz=0;
    return New++;
}

void Read(int& x){
    char tt=getchar();
    while(tt<'0'||'9'<tt) tt=getchar();
    for(x=0;'0'<=tt&&tt<='9';x=(x<<1)+(x<<3)+tt-'0',tt=getchar());
}

void Find_heavy_edge(int root){
    queue <int> Q;
    vector <int> Ts;
    Q.push(root);
    Ts.p_b(root);
    while(!Q.empty()){
        int u=Q.front();
        Q.pop();
        size[u]=1;
        dep[u]=dep[fa[u]]+1;
        for(edge* p=head[u];p!=NULL;p=p->next)
            if(p->v!=fa[u]){
                fa[p->v]=u;
                Q.push(p->v);
                Ts.p_b(p->v);
            }
    }
    int n=Ts.size();
    rrep(i,n-1,0){
        int v=Ts[i];
        int u=fa[v];
        size[u]+=size[v];
        if(size[v]>size[son[u]])
            son[u]=v;
    }
}

void Connect(int root){
    int label=0;
    deque <int> Q;
    Q.p_b(root);
    top[root]=root;
    while(!Q.empty()){
        int u=Q.front();
        Q.pp_f();
        tid[u]=++label;
        if(son[u]){
            Q.p_f(son[u]);
            top[son[u]]=top[u];
        }
        for(edge* p=head[u];p!=NULL;p=p->next)
            if(p->v!=fa[u]&&p->v!=son[u]){
                top[p->v]=p->v;
                Q.p_b(p->v);
            }
    }
}

void Init(){
    int u,v;
    Read(N);
    Read(Q);
    rep(i,1,N) Read(val[i]);
    rep(i,1,N-1){
        Read(u);
        Read(v);
        Addedge(u,v);
        Addedge(v,u);
    }
}

void Insert(node* T,int key){
    rrep(i,30,0){
        T->sz++;
        int t=(key>>i)&1;
        if(!T->ch[t]) T->ch[t]=Get();
        T=T->ch[t];
    }
    T->sz++;
}

void Delete(node* T,int key){
    rrep(i,30,0){
        T->sz--;
        int t=(key>>i)&1;
        T=T->ch[t];
    }
    T->sz--;
}

void Select(int u,int v,int k){
    vector <node*> Plus;
    vector <node*> Sub;
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]])
            swap(u,v);
        int l=tid[top[u]];
        int r=tid[u];
        for(int j=r;j;j-=lowbit(j))
            Plus.p_b(root[j]);
        for(int j=l-1;j;j-=lowbit(j))
            Sub.p_b(root[j]);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v])
        swap(u,v);
    int l=tid[u];
    int r=tid[v];
    for(int j=r;j;j-=lowbit(j))
        Plus.p_b(root[j]);
    for(int j=l-1;j;j-=lowbit(j))
        Sub.p_b(root[j]);

    int n=Plus.size();
    int m=Sub.size();
    int ans=0;
    int t=0;
    rep(i,0,n-1) t+=Plus[i]->sz;
    rep(i,0,m-1) t-=Sub[i]->sz;
    if(t<k){
        cout<<error<<endl;
        return;
    }
    rrep(i,30,0){
        t=0;
        rep(j,0,n-1)
            if(Plus[j]&&Plus[j]->ch[1])
                t+=Plus[j]->ch[1]->sz;
        rep(j,0,m-1)
            if(Sub[j]&&Sub[j]->ch[1])
                t-=Sub[j]->ch[1]->sz;
        if(k<=t){
            ans+=(1<<i);
            rep(j,0,n-1) if(Plus[j]) Plus[j]=Plus[j]->ch[1];
            rep(j,0,m-1) if(Sub[j]) Sub[j]=Sub[j]->ch[1];
        }
        else{
            k-=t;
            rep(j,0,n-1) if(Plus[j]) Plus[j]=Plus[j]->ch[0];
            rep(j,0,m-1) if(Sub[j]) Sub[j]=Sub[j]->ch[0];
        }
    }
    pf("%d\n",ans);
}

void Solve(){
    Find_heavy_edge(1);
    Connect(1);
    rep(i,1,N){
        int p=tid[i];
        for(int j=p;j<=N;j+=lowbit(j)){
            if(!root[j]) root[j]=Get();
            Insert(root[j],val[i]);
        }
    }

    int k,u,v;
    while(Q--){
        Read(k);
        Read(u);
        Read(v);
        if(!k){
            int p=tid[u];
            for(int j=p;j<=N;j+=lowbit(j)){
                Delete(root[j],val[u]);
                Insert(root[j],v);
            }
            val[u]=v;
        }
        else Select(u,v,k);
    }
}

int main(){
    Init();
    Solve();
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值