bzoj3720: Gty的妹子树

题目链接

bzoj3720

题意

维护一棵初始有n个节点的有根树(根节点为1),树上节点编号为1-n,每个点有一个权值wi。
支持以下操作:
0 u x 询问以u为根的子树中,严格大于x的值的个数。(u^=lastans,x^=lastans)
1 u x 把u节点的权值改成x。(u^=lastans,x^=lastans)
2 u x 添加一个编号为”当前树中节点数+1”的节点,其父节点为u,其权值为x。(u^=lastans,x^=lastans)
最开始时lastans=0。(强制在线)
(1<=n<=30000,1<=m<=30000)

题解

与子树相关的问题我们能想到树剖,dfs序等东西。但是这道题要支持加点与大于x数的个数的统计。这样就不是很好做了。
但是注意到修改是单点的,并且n不是非常大。我们可以分块乱搞。
我们先确定一个块的大小S,将树分成若干个联通块,每块的大小都不超过S。
我们在每个块内将权值从小到大排序。
询问时,若在一个整块内,我们二分查找,否则暴力。
修改是暴力维护块内的权值有序。
加点时判断父亲所在块大小是否等于S,等于的话该点单独成一块,否则加入父亲所在块内,注意也要维护块内权值有序。
由于有二分操作,块的大小取 nlog2n 最合适。(我也不会证明,但亲测是这样)。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;

#define N 60010
#define M 60010
struct node{
    int pos,top,data,fa;
}t[N];
struct block{
    int size;
    vector<int>v;
}b[M];
struct edge{
    int x,next;
}e[N*2][2];
int x,y,n,m,tot[2],num,first[N][2],ans,lim,op;
queue<int>q;

void add(int x,int y,int k){
    e[++tot[k]][k].x=y;
    e[tot[k]][k].next=first[x][k];
    first[x][k]=tot[k];
}

void newnode(int x,int top,int pos){
    b[pos].v.push_back(t[x].data);
    b[pos].size++;
    t[x].pos=pos;
    t[x].top=top;
}

void bfs(){
    newnode(1,1,++num); q.push(1);
    while(!q.empty()){
        x=q.front(); q.pop();
        for(int i=first[x][0];i;i=e[i][0].next)
        if(e[i][0].x!=t[x].fa){
            if(b[t[x].pos].size>=lim){
                newnode(e[i][0].x,e[i][0].x,++num);
                add(t[x].pos,num,1);
            }else newnode(e[i][0].x,t[x].top,t[x].pos);
            t[e[i][0].x].fa=x;
            q.push(e[i][0].x);
        }
    }
}

int count(int x,int y){
    int l=0,r=b[x].v.size(),mid;
    while((r-l)>1){
        mid=(l+r)>>1;
        if(b[x].v[mid]>y)r=mid;
        else l=mid;
    }
    if(b[x].v[l]>y)r=l;
    int tmp=b[x].v.size()-r;
    for(int i=first[x][1];i;i=e[i][1].next)
    tmp+=count(e[i][1].x,y);
    return tmp;
}

int ask(int x,int y){
    if(t[x].top==x)return count(t[x].pos,y);
    int tmp=(t[x].data>y);
    for(int i=first[x][0];i;i=e[i][0].next)
    if(e[i][0].x!=t[x].fa) tmp+=ask(e[i][0].x,y);
    return tmp;
}

void modify(int x,int y){
    int p=t[x].pos,n=b[p].v.size(),i;
    for(i=0;i<n;i++)
    if(b[p].v[i]==t[x].data)break;
    t[x].data=b[p].v[i]=y;
    for(;i<n-1&&b[p].v[i]>b[p].v[i+1];i++)
    swap(b[p].v[i],b[p].v[i+1]);
    for(;i>0&&b[p].v[i]<b[p].v[i-1];i--)
    swap(b[p].v[i],b[p].v[i-1]);
}

void insert(int x,int y){
    add(x,y,0);
    if(b[t[x].pos].size>=lim){
        newnode(y,y,++num);
        add(t[x].pos,num,1);
    }else newnode(y,t[x].top,t[x].pos);
    int i=b[t[y].pos].v.size()-1;
    for(;i>0&&b[t[y].pos].v[i]<b[t[y].pos].v[i-1];i--)
    swap(b[t[y].pos].v[i],b[t[y].pos].v[i-1]);
}

int main(){

    scanf("%d",&n);
    for(int i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        add(x,y,0);
        add(y,x,0);
    }
    for(int i=1;i<=n;i++) scanf("%d",&t[i].data);
    lim=(int)sqrt(n)*(log(n)/log(2));
    bfs();
    for(int i=1;i<=num;i++)sort(b[i].v.begin(),b[i].v.end());
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&op,&x,&y);
        x^=ans; y^=ans;
        if(op==0) ans=ask(x,y),printf("%d\n",ans);
        else if(op==1)modify(x,y);
        else if(op==2) {
            t[++n].data=y;
            insert(x,n);
        }
    }
    return 0;
}

你可能感兴趣的:(bzoj3720: Gty的妹子树)