【BZOJ4129】Haruna’s Breakfast,树上带修莫队+权值分块求mex

Time:2016.09.08
Author:xiaoyimi
转载注明出处谢谢


思路:
这道题相当于把昨天学的树上莫队和带修莫队融合了一下,顺便加了一个mex(未出现的最小自然数)
那么主要问题就是如何求mex
聪哥给出的思路是对[0,n]权值分块,大于n的权值不用管,因为它们永远不会影响答案(显然)
记录每个块的大小,每一次的移动使得该点权值数量+1或-1,记录这个移动对该点权值所在块大小的影响,也就是说如果这个块中该点权值出现次数为0(1),移动使数量+1(-1),那么块大小-1(+1)
这样的话做到了修改是 O(1)
查询时从小到大找到第一个大小不为0的块,然后在该块内部查找最小权值就行了
块的大小是 n23
带修莫队复杂度为 O(n53)
注意修改时要修改该点是否在当前已找的集合内,不能因为它的权值>n就不改了
话说我真的不适合用实时倍增求LCA,看来以后还是回归老本行——ST表吧= =
代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
#define M 50005
using namespace std;
int n,m,tot,cnt1,cnt2=1;
int a[M],first[M],last[M],block[M],sum[M],belong[M],siz[M],dfn[M],fa[M][16],dep[M],ans[M];
bool vis[M];
struct edge{
    int v,next;
}e[M<<1];
struct query{
    int l,r,t,id;
}q[M];
struct update{
    int pos,pre,sub;
}c[M];
int in()
{
    char ch=getchar();int t=0;
    while (ch>'9'||ch<'0') ch=getchar();
    while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48,ch=getchar();
    return t;
}
void add(int x,int y)
{
    e[++tot]=(edge){y,first[x]};first[x]=tot;
    e[++tot]=(edge){x,first[y]};first[y]=tot;   
}
bool cmp(query a,query b)
{
    if (block[a.l]==block[b.l]&&a.r==b.r) return a.t<b.t;
    if (block[a.l]==block[b.l]) return dfn[a.r]<dfn[b.r];
    return block[a.l]<block[b.l];
}
void dfs(int x)
{
    dfn[x]=++dfn[0];
    for (int i=first[x];i;i=e[i].next)
        if (e[i].v!=fa[x][0])
            fa[e[i].v][0]=x,
            dep[e[i].v]=dep[x]+1,
            dfs(e[i].v);
}
int LCA(int x,int y)
{
    if (dep[x]<dep[y]) swap(x,y);
    for (int i=15;i>=0;--i)
        if (fa[x][i]&&dep[fa[x][i]]>=dep[y])
            x=fa[x][i];
    if (x==y) return x;
    for (int i=15;i>=0;--i)
        if (fa[x][i]&&fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
void Point(int x)
{
    if (a[x]>n) {vis[x]^=1;return;}
    if (vis[x]) siz[belong[a[x]]]+=(--sum[a[x]]==0);
    else siz[belong[a[x]]]-=(++sum[a[x]]==1);
    vis[x]^=1;
}
void Path(int x,int y)
{
    if (dep[x]<dep[y]) swap(x,y);
    for (;dep[x]>dep[y];x=fa[x][0]) Point(x);
    for (;x!=y;x=fa[x][0],y=fa[y][0]) Point(x),Point(y);
}
int cal()
{
    int i,j,t=sqrt(n+1);
    for (i=0;i<=t;++i)
        if (siz[i]) break;
    for (j=i*t;j<(i+1)*t;++j)
        if (!sum[j]) return j;
}
main()
{
    n=in();m=in();
    int tt=pow(n,2.0/3.0);
    for (int i=1;i<=n;++i)
        last[i]=a[i]=in();
    for (int i=1;i<n;++i) add(in(),in());
    dfs(1);
    for (int i=1;i<=n;++i) block[i]=(dfn[i]+1)/tt;  
    for (int i=1;i<=15;++i)
        for (int j=1;j<=n;++j)
            fa[j][i]=fa[fa[j][i-1]][i-1];
    tt=sqrt(n+1);
    for (int i=0;i<=n;++i)
        belong[i]=i/tt,
        ++siz[belong[i]];
    int x,y;
    for (int i=1;i<=m;++i)
        if (in())
            q[++cnt1]=(query){in(),in(),cnt2,cnt1};
        else
            x=in(),y=in(),
            c[++cnt2]=(update){x,last[x],y},last[x]=y;
    sort(q+1,q+cnt1,cmp);
    int T=1,L=1,R=1;Point(1);
    for (int i=1;i<=cnt1;++i)
    {
        for (int j=T+1;j<=q[i].t;++j)
            if (vis[c[j].pos])
            {
                Point(c[j].pos);
                a[c[j].pos]=c[j].sub;
                Point(c[j].pos);
            }
            else a[c[j].pos]=c[j].sub;
        for (int j=T;j>q[i].t;--j)
            if (vis[c[j].pos])
            {
                Point(c[j].pos);
                a[c[j].pos]=c[j].pre;
                Point(c[j].pos);
            }
            else a[c[j].pos]=c[j].pre;
        Path(q[i].l,L);Path(q[i].r,R);
        Point(LCA(L,R));
        Point(LCA(q[i].l,q[i].r));
        ans[q[i].id]=cal();
        T=q[i].t;L=q[i].l;R=q[i].r;
    }
    for (int i=1;i<=cnt1;++i) printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值