BZOJ 4129 Haruna’s Breakfast - 带修树上莫队+分块

感觉关于树的不管是啥都200+行。。。一口老血。。。

首先树上分个块(大概就是BZOJ1086王室联邦的套路,此处应有链接,可惜这道题还在坑里233),然后每次暴力移动点,尤其是关于lca的处理需要很注意(见下图):


(假装有图)


然后这里需要求mex值。对于mex值暴力当然会TLE,于是需要分块。将n个数分成sqrt(n),查询时从头查看哪个块数未满,然后在块中暴力寻找数,复杂度O(sqrt n)。


千万要记得,当一个数修改为大于n时,此数必然不会成为mex数,可以直接跳过,数组开到maxn即可,以免造成MLE。


#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
 
using namespace std;
 
const int maxn=200005;
 
struct edge
{
    int to,next;
}e[maxn<<1];
struct query
{
    int l,r,id,t;
}q[maxn];
struct change
{
    int pre,sub,pos;
}c[maxn];
 
int n,m,cnt,tot,numblock;
int bcnt,block;
int head[maxn];
int s[maxn],last[maxn],ans[maxn];
int size[maxn],belong[maxn],depth[maxn];
bool inpath[maxn];
int num[maxn],bnum[maxn];
int ff[maxn][22];
 
bool cmp(query a,query b)
{
    return belong[a.l]<belong[b.l]||(belong[a.l]==belong[b.l]&&belong[a.r]<belong[b.r])
            ||(belong[a.l]==belong[b.l]&&belong[a.r]==belong[b.r]&&a.t<b.t);
}
void insert(int a,int b)
{
    static int ecnt=0;
    e[++ecnt].to=b;e[ecnt].next=head[a];head[a]=ecnt;
}
void dfs(int x,int fa)
{
    static int stack[maxn],top,rear;
    int bottom=stack[rear];
    stack[++rear]=x;
    size[x]=1;
    ff[x][0]=fa;
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=fa)
    {
        depth[e[i].to]=depth[x]+1;
        dfs(e[i].to,x);
        size[x]+=size[e[i].to];
        if(size[x]>=block)
        {
            ++bcnt;
            while(stack[rear]!=bottom)
            {
                int t=stack[rear--];
                belong[t]=bcnt;
            }
        }
    }
}
void update(int x)
{
    inpath[x]=true;
    if(s[x]>n)return;//
    if(!num[s[x]])bnum[s[x]/numblock]++;
    num[s[x]]++;
}
void downdate(int x)
{
    inpath[x]=false;
    if(s[x]>n)return;//
    if(num[s[x]]==1)bnum[s[x]/numblock]--;
    num[s[x]]--;
}
void xorpath(int x,int y)
{
    if(depth[x]<depth[y])swap(x,y);
    while(depth[x]>depth[y])
    {
        if(inpath[x])downdate(x);
        else update(x);
        x=ff[x][0];
    }
    while(x!=y)
    {
        if(inpath[x])downdate(x);
        else update(x);
        x=ff[x][0];
        if(inpath[y])downdate(y);
        else update(y);
        y=ff[y][0];
    }
}
int lca(int x,int y)
{
    if(depth[x]<depth[y])swap(x,y);
    int pos=0,delta=depth[x]-depth[y];
    while(delta)
    {
        if(delta&1)x=ff[x][pos];
        pos++;
        delta>>=1;
    }
    if(x==y)return x;
    for(int i=20;i>=0;i--)
        if(ff[x][i]!=ff[y][i])
            x=ff[x][i],y=ff[y][i];
    return ff[x][0];
}
void st_chart()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=20;j++)
            ff[i][j]=ff[ff[i][j-1]][j-1];
}
int getans()
{
    for(int i=0;;i++)if(bnum[i]<numblock)
        for(int j=i*numblock;;j++)if(!num[j])
            return j;
}
int main()
{
    scanf("%d%d",&n,&m);
    block=pow(n,2.0/3.0)+1+1e-7;
    numblock=sqrt(n)+1+1e-7;
    for(int i=1;i<=n;i++)
        scanf("%d",s+i),last[i]=s[i];
    for(int i=1;i<n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        insert(a,b);
        insert(b,a);
    }
    depth[1]=1;
    dfs(1,0);
    st_chart();
     
    for(int i=1;i<=m;i++)
    {
        int a,b,d;
        scanf("%d%d%d",&d,&a,&b);
        if(d==0)
        {
            c[++tot].pos=a;
            c[tot].pre=last[a];
            last[a]=b;
            c[tot].sub=b;
        }
        else
        {
            q[++cnt].l=a;
            q[cnt].r=b;
            q[cnt].id=cnt;
            q[cnt].t=tot;
        }
    }
     
    sort(q+1,q+cnt+1,cmp);
    int l=1,r=1,t=0;
     
    for(int i=1;i<=cnt;i++)
    {
        while(t>q[i].t)
        {
            int pos=c[t].pos;
            if(inpath[pos])
            {
                downdate(pos);
                s[pos]=c[t].pre;
                update(pos);
            }
            else
                s[pos]=c[t].pre;
            t--;
        }
        while(t<q[i].t)
        {
            t++;
            int pos=c[t].pos;
            if(inpath[pos])
            {
                downdate(pos);
                s[pos]=c[t].sub;
                update(pos);
            }
            else
                s[pos]=c[t].sub;
        }
        int LCA=lca(q[i].l,q[i].r);
        xorpath(q[i].l,l);
        xorpath(q[i].r,r);
        l=q[i].l;
        r=q[i].r;
        update(LCA);
        ans[q[i].id]=getans();
        downdate(LCA);
    }
    for(int i=1;i<=cnt;i++)
        printf("%d\n",ans[i]);
    return 0;
} 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值