BZOJ4336: BJOI2015 骑士的旅行

BZOJ4336: BJOI2015 骑士的旅行

Description

在一片古老的土地上,有一个繁荣的文明。
这片大地几乎被森林覆盖,有N座城坐落其中。
巧合的是,这N座城由恰好N-1条双向道路连接起来,使得任意两座城都是连通的。
也就是说,这些城形成了树的结构,任意两座城之间有且仅有一条简单路径。
在这个文明中,骑士是尤其受到尊崇的职业。
任何一名骑士,都是其家族乃至家乡的荣耀。
Henry从小就渴望成为一名能守护家乡、驱逐敌人的骑士。
勤奋训练许多年后,Henry终于满18岁了。
他决定离开家乡,向那些成名已久的骑士们发起挑战!
根据Henry的调查,大陆上一共有M名受封骑士,不妨编号为1到M。
第i个骑士居住在城Pi,武力值为Fi。
Henry计划进行若干次旅行,每次从某座城出发沿着唯一的简单路径前往另一座城,同时会挑战路线上武力值最高的K个骑士(Henry的体力有限,为了提高水平,当然要挑战最强的骑士)。
如果路线上的骑士不足K人,Henry会挑战遇到的所有人。
每次旅行前,可能会有某些骑士的武力值或定居地发生变化,Henry自然会打听消息,并对计划做出调整。
为了在每次旅行时做好充分准备,Henry希望你能帮忙在每次旅行前计算出这条路线上他将挑战哪些对手。

Input

第一行,一个整数N,表示有N座城,编号为1~N。
接下来N-1行,每行两个整数Ui和Vi,表示城Ui和城Vi之间有一条道路相连。
第N+1行,一个整数M,表示有M个骑士。
接下来M行,每行两个整数Fi和Pi。
按顺序依次表示编号为1~M的每名骑士的武力值和居住地。
第N+M+2行,两个整数Q,K,分别表示操作次数和每次旅行挑战的骑士数目上限。
接下来Q行,每行三个整数Ti,Xi,Yi。Ti取值范围为{1,2,3},表示操作类型。
一共有以下三种类型的操作:
Ti=1时表示一次旅行,Henry将从城Xi出发前往城市Yi;
Ti=2时表示编号为Xi的骑士的居住地搬到城Yi;
Ti=3时表示编号为Xi的骑士的武力值修正为Yi。

Output

输出若干行,依次为每个旅行的答案。
对每个Ti=1的询问,输出一行,按从大到小的顺序输出Henry在这次旅行中挑战的所有骑士的武力值。
如果路线上没有骑士,输出一行,为一个整数-1。

Sample Input

5
1 2
1 3
2 4
2 5
4
10 1
6 1
14 5
7 3
5 3
1 2 3
1 5 3
1 4 4
2 1 4
1 2 3

Sample Output

10 7 6
14 10 7
-1
7 6

HINT

100%的数据中,1 ≤ N, M ≤ 40,000,1 ≤ Ui, Vi, Pi ≤ N,1 ≤ Q ≤ 80,000, 1 ≤ K ≤ 20,旅行次数不超过 40,000 次,武力值为不超过1,000的正整数。 
题解Here!
看了许多巨佬的博客,不是线段树+multiset,就是LCT+配对堆,弄得我好尴尬。。。
然后就自己YY了一个树链剖分+树状数组套主席树。。。
树上问题,直接一发树链剖分转换成序列上的问题。
将每一个骑士所在结点编号插入到对应的链剖过后的新编号的树状数组上。
每一次询问,提取出对应的区间所在的(log2n)个结点,然后进行k次二分,找到相应的k大值。
修改就暴力在树状数组上修改logn次即可。
所以预处理O(mlog2n),单次修改O(log2n),单次查询O(logn*logk*logx),x为骑士的武力值,上限1,000。
还是蛮稳的。。。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 50010
#define MAXM 1000
using namespace std;
int n,m,q,k,c=1,d=1;
int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],top[MAXN];
int root[MAXN];
struct Graph{
    int next,to;
}a[MAXN<<1];
struct Knight{
    int f,p;
}knight[MAXN];
inline int read(){
    int date=0,w=1;char c=0,last=0;
    while(c<'0'||c>'9'){last=c;c=getchar();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    if(last=='-')w=-1;
    return date*w;
}
namespace CT{
    int size=1;
    int s1,s2,lrt[MAXN],rrt[MAXN],left[MAXN],right[MAXN];
    struct Charman_Tree{
        int l,r,sum;
    }a[MAXN*300];
    void insert(int k,int v,int l,int r,int &rt){
        a[size]=a[rt];rt=size++;
        a[rt].sum+=v;
        if(l==r)return;
        int mid=l+r>>1;
        if(k<=mid)insert(k,v,l,mid,a[rt].l);
        else insert(k,v,mid+1,r,a[rt].r);
    }
    int query(int l,int r,int k){
        if(l==r)return l;
        int mid=l+r>>1,t=0;
        for(int i=1;i<=s1;i++)t-=a[a[lrt[i]].l].sum;
        for(int i=1;i<=s2;i++)t+=a[a[rrt[i]].l].sum;
        if(k<=t){
            for(int i=1;i<=s1;i++)lrt[i]=a[lrt[i]].l;
            for(int i=1;i<=s2;i++)rrt[i]=a[rrt[i]].l;
            return query(l,mid,k);
        }
        else{
            for(int i=1;i<=s1;i++)lrt[i]=a[lrt[i]].r;
            for(int i=1;i<=s2;i++)rrt[i]=a[rrt[i]].r;
            return query(mid+1,r,k-t);
        }
    }
    inline void init(){s1=s2=0;}
    inline int lowbit(int x){return x&(-x);}
    inline void update(int x,int k,int v){for(;x<=n;x+=lowbit(x))insert(k,v,1,MAXM,root[x]);}
    inline void add(int l,int r){
        for(int i=l-1;i;i-=lowbit(i))lrt[++s1]=root[i];
        for(int i=r;i;i-=lowbit(i))rrt[++s2]=root[i];
    }
    void Answer(){
        int sum=0,width;
        for(int i=1;i<=s1;i++){sum-=a[lrt[i]].sum;left[i]=lrt[i];}
        for(int i=1;i<=s2;i++){sum+=a[rrt[i]].sum;right[i]=rrt[i];}
        width=min(sum,k);
        if(width==0){
            printf("-1\n");
            return;
        }
        for(int i=1;i<=width;i++){
            for(int j=1;j<=s1;j++)lrt[j]=left[j];
            for(int j=1;j<=s2;j++)rrt[j]=right[j];
            printf("%d ",query(1,MAXM,sum-i+1));
        }
        printf("\n");
    }
}
inline void add(int x,int y){
    a[c].to=y;a[c].next=head[x];head[x]=c++;
    a[c].to=x;a[c].next=head[y];head[y]=c++;
}
void dfs1(int rt){
    son[rt]=0;size[rt]=1;
    for(int i=head[rt];i;i=a[i].next){
        int will=a[i].to;
        if(!deep[will]){
            deep[will]=deep[rt]+1;
            fa[will]=rt;
            dfs1(will);
            size[rt]+=size[will];
            if(size[son[rt]]<size[will])son[rt]=will;
        }
    }
}
void dfs2(int rt,int f){
    id[rt]=d++;top[rt]=f;
    if(son[rt])dfs2(son[rt],f);
    for(int i=head[rt];i;i=a[i].next){
        int will=a[i].to;
        if(will!=fa[rt]&&will!=son[rt])
        dfs2(will,will);
    }
}
void solve(int x,int y){
    CT::init();
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]])swap(x,y);
        CT::add(id[top[x]],id[x]);
        x=fa[top[x]];
    }
    if(deep[x]>deep[y])swap(x,y);
    CT::add(id[x],id[y]);
    CT::Answer();
    return;
}
void work(){
    int f,x,y;
    while(q--){
        f=read();x=read();y=read();
        if(f==1){
            solve(x,y);
        }
        else if(f==2){
            CT::update(id[knight[x].p],knight[x].f,-1);
            knight[x].p=y;
            CT::update(id[knight[x].p],knight[x].f,1);
        }
        else{
            CT::update(id[knight[x].p],knight[x].f,-1);
            knight[x].f=y;
            CT::update(id[knight[x].p],knight[x].f,1);
        }
    }
}
void init(){
    int x,y;
    n=read();
    for(int i=1;i<n;i++){
        x=read();y=read();
        add(x,y);
    }
    deep[1]=1;
    dfs1(1);
    dfs2(1,1);
    m=read();
    for(int i=1;i<=m;i++){
        knight[i].f=read();knight[i].p=read();
        CT::update(id[knight[i].p],knight[i].f,1);
    }
    q=read();k=read();
}
int main(){
    init();
    work();
    return 0;
}

  

posted @ 2018-07-27 11:00  符拉迪沃斯托克  阅读(301)  评论(0编辑  收藏  举报
Live2D