bzoj 3720: Gty的妹子树 树分块

题意

给出一棵n个节点的树,节点1为根节点,每个节点有一个权值,要求资瓷三个操作:
0 x y表示询问以x为根的子树内有多少个节点的权值大于y
1 x y表示将节点x的权值修改为y
2 x y表示插入一个新节点,其父节点为x,权值为y
n,m<=30000

分析

看到这个数据范围如果是序列操作的话就毫不犹豫上分块了,但这是一棵树,所以一开始的想法就是用树剖或者主席树什么的搞一搞,但是插入操作太恶心了,根本资瓷不了。所以就跑去学了一波树分块。

树分块好劲啊!!!

分块的方法是,对于某个节点,若它父亲所在的块还没满则分进父亲所在的块,否则就自成一块,然后向其父亲所在的块连边。
然后就可以转换成对序列操作了。
询问的时候对不完整的块暴力查询,对完整的块二分查找即可。

注意分块数组要开的贼大。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 60005
using namespace std;

int cnt,last[N],block_last[N],pos[N],a[N*400],w[N],tot[N],fa[N],block,tot_block,n,m;
struct edge{int to,next;}e[N*10];

void addedge(int u,int v)
{
    e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
    e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}

void block_addedge(int u,int v)
{
    e[++cnt].to=v;e[cnt].next=block_last[u];block_last[u]=cnt;
}

void build_block(int x)
{
    for (int i=last[x];i;i=e[i].next)
    {
        if (e[i].to==fa[x]) continue;
        fa[e[i].to]=x;
        if (tot[pos[x]]<block)
        {
            pos[e[i].to]=pos[x];
            tot[pos[x]]++;
            a[(pos[x]-1)*block+tot[pos[x]]]=w[e[i].to];
        }else
        {
            pos[e[i].to]=++tot_block;
            tot[tot_block]=1;
            a[(pos[e[i].to]-1)*block+1]=w[e[i].to];
            block_addedge(pos[x],pos[e[i].to]);
        }
        build_block(e[i].to);
    }
}

void insert(int x)
{
    if (tot[pos[fa[x]]]<block)
    {
        pos[x]=pos[fa[x]];
        tot[pos[x]]++;
        a[(pos[x]-1)*block+tot[pos[x]]]=w[x];
        for (int i=(pos[x]-1)*block+tot[pos[x]];i>(pos[x]-1)*block+1&&a[i]<a[i-1];i--)
            swap(a[i],a[i-1]);
    }else
    {
        pos[x]=++tot_block;
        tot[tot_block]=1;
        block_addedge(pos[fa[x]],pos[x]);
        a[(pos[x]-1)*block+1]=w[x];
    }
}

void modify(int pos,int x,int y)
{
    int u=lower_bound(a+(pos-1)*block+1,a+(pos-1)*block+tot[pos]+1,x)-a;
    a[u]=y;
    int i=u;
    for (int i=u;i<(pos-1)*block+tot[pos]&&a[i]>a[i+1];i++)
        swap(a[i],a[i+1]);
    for (int i=u;i>(pos-1)*block+1&&a[i]<a[i-1];i--)
        swap(a[i],a[i-1]);
}

int block_query(int x,int y)
{
    int u=upper_bound(a+(x-1)*block+1,a+(x-1)*block+tot[x]+1,y)-a,ans=0;
    if (u<=(x-1)*block+tot[x]) ans+=(x-1)*block+tot[x]-u+1;
    for (int i=block_last[x];i;i=e[i].next)
        ans+=block_query(e[i].to,y);
    return ans;
}

int query(int x,int y)
{
    int ans=0;
    if (w[x]>y) ans++;
    for (int i=last[x];i;i=e[i].next)
    {
        if (e[i].to==fa[x]) continue;
        if (pos[e[i].to]==pos[x]) ans+=query(e[i].to,y);
        else ans+=block_query(pos[e[i].to],y);
    }
    return ans;
}

int main()
{
    scanf("%d",&n);
    block=sqrt(n);
    for (int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);
    }
    for (int i=1;i<=n;i++)
        scanf("%d",&w[i]);
    pos[1]=1;
    tot[1]=1;
    tot_block=1;
    a[1]=w[1];
    build_block(1);
    for (int i=1;i<=tot_block;i++)
        sort(a+(i-1)*block+1,a+(i-1)*block+tot[i]+1);
    scanf("%d",&m);
    int lastans=0;
    for (int i=1;i<=m;i++)
    {
        int x,y,op;
        scanf("%d%d%d",&op,&x,&y);
        x^=lastans;y^=lastans;
        if (op==0)
        {
            lastans=query(x,y);
            printf("%d\n",lastans);
        }else if (op==1)
        {
            modify(pos[x],w[x],y);
            w[x]=y;
        }else
        {
            w[++n]=y;
            addedge(x,n);
            fa[n]=x;
            insert(n);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值