BZOJ 3720 gty的妹子树

块状树裸题

块状树:

首先对树进行分块,分出的每一块都是一个连通块

通常的分块的方式如下:

1、父亲所在块不满,分到父亲所在块中

2、父亲所在块满,自己单独开一个块

(貌似有更为优越的分块方式?

注意这是不严格的分块,即每个块的大小不一定都是设定的阈值blo

对于这道题,首先修改和添加直接块内暴力就可以了

对于询问,首先我们对每个块排序,块不完全在询问子树内的单点询问,块完全在询问子树内的块内二分

分块真是骗分利器,但是块状树最大的缺点是容易被菊花树卡住

(貌似修改和添加可以去掉一个log,但是我太弱了,并不想写)

 

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath> 
using namespace std;
 
const int oo=0x7fffffff;
const int maxn=200010;
int n,m,u,v,f,ans,tot,blo;
int pos[maxn],fa[maxn],w[maxn];
int a[10010][510],cnt[10010];
struct Graph{
    int h[maxn],next[maxn],to[maxn],sum;
    void add(int x,int y){++sum;next[sum]=h[x];h[x]=sum;to[sum]=y;}
}G,T;
void read(int &num){
    num=0;char ch=getchar();
    while(ch<'!')ch=getchar();
    while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
bool cmp(const int &i,const int &j){return i>j;}
void check_block(int u){
    int k=pos[fa[u]];
    if(cnt[k]==blo){
        pos[u]=++tot;a[tot][0]=oo;
        a[tot][++cnt[tot]]=w[u];
        T.add(k,tot);
    }else a[k][++cnt[k]]=w[u],pos[u]=k;
    for(int i=G.h[u];i;i=G.next[i]){
        if(G.to[i]==fa[u])continue;
        fa[G.to[i]]=u;
        check_block(G.to[i]);
    }return;
}
int Get_pos(int u){
    int L=0,R=cnt[u];
    while(L<R){
        int mid=L+((R-L+1)>>1);
        if(a[u][mid]>v)L=mid;
        else R=mid-1;
    }return L;
}
void Get_block(int u){
    ans+=Get_pos(u);
    for(int i=T.h[u];i;i=T.next[i])Get_block(T.to[i]);
}
void Get_ask(int u){
    if(w[u]>v)ans++;
    for(int i=G.h[u];i;i=G.next[i]){
        if(G.to[i]==fa[u])continue;
        if(pos[G.to[i]]==pos[u])Get_ask(G.to[i]);
        else Get_block(pos[G.to[i]]);
    }return;
}
void Get_insert(int u,int k){
    pos[u]=k;w[u]=v;a[k][++cnt[k]]=v;
    sort(a[k]+1,a[k]+cnt[k]+1,cmp);
}
void Get_modify(int u){
    int k=pos[u];
    for(int i=1;i<=cnt[k];++i){
        if(a[k][i]==w[u]){a[k][i]=v;break;}
    }
    w[u]=v;
    sort(a[k]+1,a[k]+cnt[k]+1,cmp);
}
 
int main(){
    read(n);
    for(int i=1;i<n;++i){
        read(u);read(v);
        G.add(u,v);G.add(v,u);
    }
    for(int i=1;i<=n;++i)read(w[i]);
    blo=(int)(sqrt(n));
    fa[1]=1;pos[1]=1;tot=1;
    check_block(1);
    for(int i=1;i<=tot;++i)sort(a[i]+1,a[i]+cnt[i]+1,cmp);
    //for(int i=1;i<=n;++i)cout<<pos[i]<<endl;
    //cout<<endl;
    read(m);
    while(m--){
        read(f);read(u);read(v);
        u^=ans;v^=ans;
        if(f==0){
            ans=0;
            Get_ask(u);
            printf("%d\n",ans);
        }else if(f==2){
            n++;fa[n]=u;G.add(u,n);
            if(cnt[pos[u]]==blo){
                pos[n]=++tot;w[n]=v;a[tot][0]=oo;
                a[tot][++cnt[tot]]=w[n];
                T.add(pos[u],tot);
            }else Get_insert(n,pos[u]);
        }else Get_modify(u);
    }return 0;
}
gty的妹子树

 

转载于:https://www.cnblogs.com/joyouth/p/5333238.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值