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

Description

Haruna每天都会给提督做早餐! 这天她发现早饭的食材被调皮的 Shimakaze放到了一棵

树上,每个结点都有一样食材,Shimakaze要考验一下她。
每个食材都有一个美味度,Shimakaze会进行两种操作:
1、修改某个结点的食材的美味度。
2、对于某条链,询问这条链的美味度集合中,最小的未出现的自然数是多少。即mex值。
请你帮帮Haruna吧。
Input

第一行包括两个整数n,m,代表树上的结点数(标号为1~n)和操作数。

第二行包括n个整数a1…an,代表每个结点的食材初始的美味度。
接下来n-1行,每行包括两个整数u,v,代表树上的一条边。
接下来m 行,每行包括三个整数
0 u x 代表将结点u的食材的美味度修改为 x。
1 u v 代表询问以u,v 为端点的链的mex值。
Output

对于每次询问,输出该链的mex值。

题解:

mex问题我们可以容易对数字进行分块+莫队从而解决,详情见BZOJ3585(权限题)。到了树上,我们可以考虑用树上莫队,又由于有修改,我们用带修改莫队。做法都差不多,我是参考neither_nor的代码的。有几个地方要注意一下:1、树上的分块,每块的大小是 n23 ,我也不知道为什么……2、从 (L,R) (X,Y) 的处理,看一下代码,脑补一下就差不多了。

代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=50010;
struct Edge{int y,next;}e[maxn*2];
int last[maxn],len=0;
void ins(int x,int y)
{
    int t=++len;
    e[t].y=y;e[t].next=last[x];last[x]=t;
}
int dep[maxn],fa[maxn][18];
int sta[maxn],top=0,bl[maxn],MX,cnt=0;
int n,m,belong[maxn],c[maxn],block[250],l[250],r[250],vis[maxn],a[maxn];
void dfs(int x)
{
    dep[x]=dep[fa[x][0]]+1;
    for(int i=1;(1<<i)<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
    int now=top;
    for(int i=last[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(y==fa[x][0])continue;
        fa[y][0]=x;dfs(y);
        if(top-now>MX)
        {
            cnt++;
            while(top>now)bl[sta[top--]]=cnt;
        }
    }
    sta[++top]=x;
    if(x==1)
    {
        cnt++;
        while(top)bl[sta[top--]]=cnt;
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);//x jump
    for(int i=17;i>=0;i--)
    if((1<<i)<=dep[x]-dep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=17;i>=0;i--)
    if(dep[x]>=(1<<i)&&fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
void add(int x)
{
    if(a[x]>n)return;
    c[a[x]]++;
    if(c[a[x]]==1)block[belong[a[x]]]++;
}
void del(int x)
{
    if(a[x]>n)return;
    c[a[x]]--;
    if(!c[a[x]])block[belong[a[x]]]--;
}
void change(int x,int y)
{
    if(vis[x])del(x),a[x]=y,add(x);
    else a[x]=y;
}
void rev(int x)
{
    if(vis[x])vis[x]=0,del(x);
    else vis[x]=1,add(x);
}
int calc()
{
    if(!c[0])return 0;
    int i;
    for(i=1;i<=belong[n];i++)
    if(block[i]!=r[i]-l[i]+1)break;
    for(int j=l[i];j<=r[i];j++)
    if(!c[j])return j;
}
struct Modify{int x,y,pre;}A[maxn];
struct Query{int x,y,time,id;}B[maxn];
bool cmp(Query a,Query b)
{
    if(bl[a.x]!=bl[b.x])return bl[a.x]<bl[b.x];
    if(bl[a.y]!=bl[b.y])return bl[a.y]<bl[b.y];
    return a.time<b.time;
}
int la=0,lb=0,ans[maxn],yl[maxn];
int main()
{
    memset(block,0,sizeof(block));
    memset(c,0,sizeof(c));
    scanf("%d%d",&n,&m);
    MX=(int)pow(1.0*n*n,1.0/3);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),yl[i]=a[i];
    int sq=(int)sqrt(n);
    for(int i=1;i<=n;i++)//对数字进行分块
    {
        belong[i]=(i-1)/sq+1;
        int w=belong[i];
        if(!l[w])l[w]=i;
        r[w]=i;
    }
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        ins(u,v);ins(v,u);
    }
    dfs(1);
    for(int i=1;i<=m;i++)
    {
        int op,x,y;
        scanf("%d%d%d",&op,&x,&y);
        if(op==0)
        {
            A[++la].x=x;A[la].y=y;
            A[la].pre=a[x];
            a[x]=y;
        }
        else
        {
            B[++lb].x=x;B[lb].y=y;
            B[lb].time=la;B[lb].id=lb;
        }
    }
    sort(B+1,B+1+lb,cmp);
    for(int i=1;i<=n;i++)a[i]=yl[i];
    int L=1,R=1;B[0].time=0;
    for(int i=1;i<=lb;i++)
    {
        int l1=LCA(B[i].x,L);
        int l2=LCA(B[i].y,R);
        for(int x=L;x!=l1;x=fa[x][0])rev(x);
        for(int x=B[i].x;x!=l1;x=fa[x][0])rev(x);
        for(int x=R;x!=l2;x=fa[x][0])rev(x);
        for(int x=B[i].y;x!=l2;x=fa[x][0])rev(x);
        L=B[i].x;R=B[i].y;
        int l=LCA(L,R);
        rev(l);
        for(int j=B[i-1].time+1;j<=B[i].time;j++)change(A[j].x,A[j].y);
        for(int j=B[i-1].time;j>B[i].time;j--)change(A[j].x,A[j].pre);
        ans[B[i].id]=calc();
        rev(l);
    }
    for(int i=1;i<=lb;i++)
    printf("%d\n",ans[i]);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值