bzoj 4129: Haruna’s Breakfast 树上带修改莫队+分块

题意

有一棵树,每个节点有点权,要求资瓷单点修改和查询一条链上的mex。
n,m<=50000

分析

一开始想到了树上带修改莫队+分块,但是觉得应该还有更简单的办法,结果发现就是这样。。。
权当是复习树上莫队和带修改莫队吧。
树上莫队就是把这棵树的括号序(注意不是欧拉序)求出来然后就变成序列莫队了。
带修改的话就是加多一维表示修改到哪里。
其他的就是裸的求mex了。
没想到打的这么顺手。。。

一开始WA的原因是算mex的时候没有把n算进去。

代码

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

const int N=50005;

int n,m,cnt,last[N],f1,f[N*2],fa[N][20],block,tot[N],t[N],a[N],bel[N*2],fir[N],sec[N],q1,c1,dep[N],sta[2005],end[2005];
bool vis[N];
struct edge{int to,next;}e[N*2];
struct que{int l,r,x,id,lca,ans;}q[N];
struct oper{int x,y;}cha[N];

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

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 dfs(int x)
{
    for (int i=1;i<=16;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    dep[x]=dep[fa[x][0]]+1;f[++f1]=x;fir[x]=f1;bel[f1]=(f1+block-1)/block;
    for (int i=last[x];i;i=e[i].next)
    {
        if (e[i].to==fa[x][0]) continue;
        fa[e[i].to][0]=x;
        dfs(e[i].to);
    }
    f[++f1]=x;sec[x]=f1;bel[f1]=(f1+block-1)/block;
}

int get_lca(int x,int y)
{
    if (dep[x]<dep[y]) swap(x,y);
    for (int i=16;i>=0;i--)
        if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    if (x==y) return x;
    for (int i=16;i>=0;i--)
        if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

bool cmp(que a,que b)
{
    return bel[a.l]<bel[b.l]||bel[a.l]==bel[b.l]&&bel[a.r]<bel[b.r]||bel[a.l]==bel[b.l]&&bel[a.r]==bel[b.r]&&bel[a.x]<bel[b.x];
}

bool cmpid(que a,que b)
{
    return a.id<b.id;
}

void updata(int x)
{
    vis[x]=1-vis[x];
    if (a[x]>n) return;
    if (!vis[x])
    {
        t[a[x]]--;
        if (!t[a[x]]) tot[bel[a[x]]]--;
    }
    else
    {
        if (!t[a[x]]) tot[bel[a[x]]]++;
        t[a[x]]++;
    }
}

void change(int x)
{
    if (vis[cha[x].x]) updata(cha[x].x),swap(a[cha[x].x],cha[x].y),updata(cha[x].x);
    else swap(a[cha[x].x],cha[x].y);
}

int query()
{
    for (int i=1;;i++)
        if (tot[i]<end[i]-sta[i]+1)
        {
            for (int j=sta[i];j<=end[i];j++)
                if (!t[j]) return j-1;
        }
}

void solve()
{
    for (int i=1,l=1,r=0,x=0;i<=q1;i++)
    {
        for (;r<q[i].r;r++) updata(f[r+1]);
        for (;l>q[i].l;l--) updata(f[l-1]);
        for (;r>q[i].r;r--) updata(f[r]);
        for (;l<q[i].l;l++) updata(f[l]);
        for (;x<q[i].x;x++) change(x+1);
        for (;x>q[i].x;x--) change(x);
        if (!vis[q[i].lca]) updata(q[i].lca),q[i].ans=query(),updata(q[i].lca);
        else q[i].ans=query();
    }
}

int main()
{
    n=read();m=read();block=pow(1.0*n*n,1.0/3);
    for (int i=1;i<=n;i++) a[i]=read()+1;
    for (int i=1;i<n;i++)
    {
        int x=read(),y=read();
        addedge(x,y);
    }
    dfs(1);
    for (int i=1;i<=n+1;i++)
    {
        if (!sta[bel[i]]) sta[bel[i]]=i;
        end[bel[i]]=i;
    }
    for (int i=1;i<=m;i++)
    {
        int op=read(),x=read(),y=read();
        if (!op) cha[++c1].x=x,cha[c1].y=y+1;
        else
        {
            q[++q1].x=c1;q[q1].lca=get_lca(x,y);q[q1].id=i;
            if (q[q1].lca==x||q[q1].lca==y) q[q1].l=min(fir[x],fir[y]),q[q1].r=max(fir[x],fir[y]);
            else
            {
                if (sec[x]<fir[y]) q[q1].l=sec[x],q[q1].r=fir[y];
                else q[q1].l=sec[y],q[q1].r=fir[x];
            }
        }
    }
    sort(q+1,q+q1+1,cmp);
    solve();
    sort(q+1,q+q1+1,cmpid);
    for (int i=1;i<=q1;i++) printf("%d\n",q[i].ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值