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

来源:互联网 发布:典型数据报表图片 编辑:程序博客网 时间:2024/05/16 15:50

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 50005using 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]);}
0 0
原创粉丝点击