[BZOJ4129]Haruna’s Breakfast(树上莫队+分块)

题目:

我是超链接

题解:

这一看就是树上莫队+修改没跑了吧,区间转移还是统计每个数字的个数吧,但是这个查询?
朴素的想法就是按照权值分块了,看看这个权值块里数值是不是全的,不是全的就枚举,这样可以实现 O(n) O ( n ) 枚举, O(1) O ( 1 ) 修改,这个块略大啊,存num也不好存,但实际上,我们只需要存[0,n]之内的权值就好了,因为只有n个数,mex肯定出在这个区间内,其他的丢掉就好
哇哇哇1A辣

代码:

#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int sz=24;
const int N=50005;
struct hh{int x,y,id,t;}ch[N],q[N];
int a[N],tot,nxt[N*2],point[N],v[N*2],pos[N],ks,nn,top,n,ans[N];
int f[N][sz],mi[sz],h[N],num[N],belong[N],have[N],dfn[N],block,stack[N],cnt;
bool vis[N];
int cmp(hh a,hh b)
{
    return pos[a.x]<pos[b.x] || (pos[a.x]==pos[b.x] && dfn[a.y]<dfn[b.y]) ||
    (pos[a.x]==pos[b.x] && dfn[a.y]==dfn[b.y] && a.t<b.t);
}
void addline(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;   
}
void dfs(int x,int fa)
{
    dfn[x]=++nn; h[x]=h[fa]+1;int bottom=top;
    for (int i=1;i<sz;i++)
      if (h[x]<mi[i]) break;
      else f[x][i]=f[f[x][i-1]][i-1];
    for (int i=point[x];i;i=nxt[i])
      if (v[i]!=fa)
      {
        f[v[i]][0]=x;dfs(v[i],x);
        if (top-bottom>=block)
        {
            cnt++;
            while (top!=bottom) pos[stack[top--]]=cnt;
        }
      }
    stack[++top]=x;
}
int lca(int x,int y)
{
    if (h[x]<h[y]) swap(x,y);
    int k=h[x]-h[y];
    for (int i=0;i<sz;i++)
      if (k&(1<<i)) x=f[x][i];
    if (x==y) return x;
    for (int i=sz-1;i>=0;i--)
      if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
void change(int x)
{
    if (vis[x])
    {
        vis[x]=0; 
        if (a[x]<n) 
        {
            num[a[x]]--;
            if (!num[a[x]]) have[belong[a[x]]]--;
        }
    }else
    {
        vis[x]=1; 
        if (a[x]<n)
        {
            num[a[x]]++;
            if (num[a[x]]==1) have[belong[a[x]]]++;
        }
    }
}
void reverse(int x,int y)
{
    while (x!=y)
      if (h[x]<h[y]) change(y),y=f[y][0];
      else change(x),x=f[x][0];
}
void modi(int now)
{
    if (vis[ch[now].x])
    {
        change(ch[now].x);
        swap(a[ch[now].x],ch[now].y);
        change(ch[now].x);
    }else swap(a[ch[now].x],ch[now].y);
}
int ask()
{
    for (int i=1;i<=ks;i++)
      if (have[i]!=block)
      {
        int l=(i-1)*block,r=i*block-1;
        for (int j=l;j<=r;j++)
          if (!num[j]) return j;
      }
    return n;
}
int main()
{
    int x,y,tim=0,wh=0,m;scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        addline(x,y);
    }
    mi[0]=1;for (int i=1;i<sz;i++) mi[i]=mi[i-1]*2;
    dfs(1,0);
    cnt++;while (top) pos[stack[top--]]=cnt;
    block=sqrt(n);ks=n/block;if (n%block) ks++;
    for (int i=0;i<n;i++) belong[i]=i/block+1; 
    for (int i=1;i<=m;i++)
    {
        int id;scanf("%d%d%d",&id,&x,&y);
        if (id==0) ch[++tim].x=x,ch[tim].y=y;
        else 
        {
            if (dfn[x]>dfn[y]) swap(x,y);
            q[++wh].x=x;q[wh].y=y;q[wh].t=tim;q[wh].id=wh;
        }
    }
    sort(q+1,q+wh+1,cmp);
    int t=lca(q[1].x,q[1].y),now=0;
    while (now<q[1].t) modi(++now);
    reverse(q[1].x,q[1].y); change(t);
    ans[q[1].id]=ask();
    for (int i=2;i<=wh;i++)
    {
        change(t); reverse(q[i-1].x,q[i].x); reverse(q[i-1].y,q[i].y);
        while (now<q[i].t) modi(++now);
        while (now>q[i].t) modi(now--);
        t=lca(q[i].x,q[i].y); change(t);
        ans[q[i].id]=ask();
    }
    for (int i=1;i<=wh;i++) printf("%d\n",ans[i]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值