BZOJ 3720 gty的妹子树

块状树裸题
首先将树分块:
如果父亲的块不是满的,就将自己添加进块里
否则自己单独形成一个块
可以证明:每个块都是一个联通块
之后考虑操作:
对于操作0,我们暴力询问就可以了
块不完全在询问范围->单点询问
块完全在询问范围->块询问
对于操作1 随便改一改就好了
对于操作2 考虑父亲的块是不是满的,随便改一改就好了

分块真是骗分的利器
然而块状树的缺点是如果有菊花树分出的块就很带感了
修改其实有不带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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值