牛客练习赛30 F 小K种妹妹(树上分块)

版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/83870571

大致题意,实现一种树状数据结构,包含修改权值,添加节点,删除边和查询子树内特定权值范围节点个数的操作。

虽然说是数据结构,而且有LCT这个东西可以考虑使用,但是分块作为一种非常快速的方法着实是赛场上的首选。树上的分块相对比较少见,本题可以作为树上分块的模板题。

树上分块与普通分块类似,只不过是要按照dfs序去分块。每次顺序dfs,把节点加入块中,当块的节点个数大于阈值,那么开辟新的块。如果树中间存在节点连接两个块,那么对应就要有一条边连接这两条块。可以预见,当分块完毕之后,原本一个含N个节点的树,会变成一个只含有大约\sqrt{N}级别个块的块状树。而每个节点内部是原本树的子树。在建树的同时,我们根据原本单个节点的数值,维护每个块的数值。

当我们需要查询时,首先在块的级别上查询,到临界点的时候再在普通节点的级别上查询。如此,当阈值设定的好的时候,我们查询的时间复杂度就是O(\sqrt{N})的。

对于删除边的操作也是类似。我们分删除边是块间边和块内边两种情况。当是块间边的时候,相对比较容易处理,直接把原本块级别上的边删除即可,不需要过多的维护块节点的数值。而当是块内边的时候,由于一条边的删除,原本的一个块会分成两个块,在删除一条边的同时,要维护两个块节点的数值。

针对本题来说,需要维护在一定数值范围内的点的个数,我们只需要对于每一个块,动态维护一个vector即可。每一次块内查找的时候二分查找即可。本题总的复杂度是O(N\sqrt{N}logN)。具体间代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pi 3.141592653589793
#define mod 998244353
#define LL long long
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define sf(x) scanf("%d",&x)
#define sc(x,y,z) scanf("%d%d%d",&x,&y,&z)

using namespace std;

const int N = 200010;

struct blocks
{
    vector<int> v;

    inline int sz(){return v.size();}
    inline void update(int x,int y){erase(x);insert(y);}
    inline void erase(int x){v.erase(lb(v.begin(),v.end(),x));}
    inline void insert(int x){v.insert(lb(v.begin(),v.end(),x+1),x);}
    inline int query(int x){return v.end()-ub(v.begin(),v.end(),x);}

} blks[N];

int blk,tot,n,q,w[N],f[N],fa[N];
int ls[N],g[N<<1],nxt[N],e=0;
int LS[N],G[N<<1],NXT[N],E=0;

inline void addedge(int x,int y){g[++e]=y;nxt[e]=ls[x];ls[x]=e;}
inline void ADDEDGE(int x,int y){G[++E]=y;NXT[E]=LS[x];LS[x]=E;}

void build(int x,int father)
{
    int now=f[x]; fa[x]=father;
    for(int i=ls[x];i;i=nxt[i])
    {
        int y=g[i];
        if (y==father) continue;
        if (blks[now].sz()+1>blk) ADDEDGE(now,f[y]);
                            else f[y]=now;
        blks[f[y]].insert(w[y]); build(y,x);
    }
}

int blk_query(int x,int lim)
{
    int res=blks[x].query(lim);
    for(int i=LS[x];i;i=NXT[i])
        res+=blk_query(G[i],lim);
    return res;
}

int query(int x,int lim)
{
    if (f[x]==x) return blk_query(x,lim);
    int res=w[x]>=lim;
    for(int i=ls[x];i;i=nxt[i])
    {
        int y=g[i];
        if (y==fa[x]||fa[y]!=x) continue;
        if (y!=f[y]) res+=query(y,lim);
            else res+=blk_query(y,lim);
    }
    return res;
}

void cut(int x,int ff)
{
    int now=f[x];
    blks[now].erase(w[x]);
    for(int i=ls[x];i;i=nxt[i])
    {
        int y=g[i];
        if (y==fa[x]||fa[y]!=x) continue;
        if (y!=f[y]) cut(y,ff); 
        else
        {
            if (G[LS[now]]==y) LS[now]=NXT[LS[now]];
            else for(int j=LS[now];j;j=NXT[j])
                    if (G[NXT[j]]==y) {NXT[j]=NXT[NXT[j]];break;}
            ADDEDGE(ff,y);
        }
            
    }
    blks[f[x]=ff].insert(w[x]);
}

int main()
{
    sf(n); blk=300;
    for(int i=1;i<n;i++)
    {
        int x,y;
        sf(x); sf(y);
        addedge(x,y); addedge(y,x);
    }
    for(int i=1;i<=n;i++)
        f[i]=i,sf(w[i]);
    blks[1].insert(w[1]);
    build(1,0); sf(q); int ans=0;
    while(q--)
    {
        int op,x,y;
        sf(op); sf(x),x^=ans;
        if (op!=3) sf(y),y^=ans;
        if (op==0) printf("%d\n",ans=query(x,y));
        else if (op==1) blks[f[x]].update(w[x],y),w[x]=y;
        else if (op==2)
        {
            int now=f[x];
            w[++n]=y; f[n]=n; fa[n]=x;
            if (blks[now].sz()+1>blk) ADDEDGE(now,f[n]);
                                    else f[n]=now;
            blks[f[n]].insert(y); addedge(x,n);
        } else if (fa[x])
        {
            if (f[x]==x)
            {
                int ff=f[fa[x]];  
                if (G[LS[ff]]==x) LS[ff]=NXT[LS[ff]];
                else for(int i=LS[ff];i;i=NXT[i])
                        if (G[NXT[i]]==x) {NXT[i]=NXT[NXT[i]];break;}
            } else cut(x,x);
            fa[x]=0; 
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/83870571