【BZOJ】4129: Haruna’s Breakfast 树分块+带修改莫队算法

【题意】给定n个节点的树,每个节点有一个数字ai,m次操作:修改一个节点的数字,或询问一条树链的数字集合的mex值。n,m<=5*10^4,0<=ai<=10^9。

【算法】树分块+带修改莫队算法

【题解】和【BZOJ】3052: [wc2013]糖果公园 树分块+待修改莫队算法差不多。

区别在于如何处理树链信息。考虑对值域分块,由于>n的数字没用,所以对[0,n]分块,维护每一块所含数字个数。

这样就可以O(1)单点修改,O(√n)查询。(扫描到第一块所含数字不满的块,再块内扫描到第一个没出现的数字)。

复杂度O(n^(5/3)+n^(3/2))。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=50010;
int belong[maxn],be[maxn],first[maxn],tot,cnt,top,deep[maxn],f[maxn][22],B,Q;
int st[maxn],c[maxn],n,m,c0,c1,sz[maxn],num[maxn],mx,pre[maxn],ANS[maxn];
bool vis[maxn];
struct edge{int v,from;}e[maxn*2];
struct mo{int x,y,pre;}a[maxn];
struct que{int x,y,t,id;}b[maxn];
bool cmp(que a,que b){return belong[a.x]<belong[b.x]||(belong[a.x]==belong[b.x]&&belong[a.y]<belong[b.y])||
(belong[a.x]==belong[b.x]&&belong[a.y]==belong[b.y]&&a.t<b.t);}
void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
void dfs(int x,int fa){
    int p=top;
    for(int j=1;(1<<j)<=deep[x];j++)f[x][j]=f[f[x][j-1]][j-1];
    for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
        deep[e[i].v]=deep[x]+1;
        f[e[i].v][0]=x;
        dfs(e[i].v,x);
        if(top-p>=B){
            cnt++;
            while(top>p)belong[st[top--]]=cnt;
        }
    }
    st[++top]=x;//
}
int lca(int x,int y){
    if(deep[x]<deep[y])swap(x,y);
    int d=deep[x]-deep[y];
    for(int j=0;(1<<j)<=d;j++)if(d&(1<<j))x=f[x][j];
    if(x==y)return x;
    for(int j=16;j>=0;j--)if((1<<j)<=deep[x]&&f[x][j]!=f[y][j])x=f[x][j],y=f[y][j];
    return f[x][0];
}
void rev(int x){
    if(c[x]>n){vis[x]^=1;return;}
    if(!vis[x])sz[be[c[x]]]+=(++num[c[x]]==1);
    else sz[be[c[x]]]-=(--num[c[x]]==0);
    vis[x]^=1;
}
void modify(int x,int y){
    if(!vis[x])c[x]=y;
    else rev(x),c[x]=y,rev(x);
}
void solve(int x,int y){
    while(x!=y){
        if(deep[x]>deep[y])rev(x),x=f[x][0];
        else rev(y),y=f[y][0];
    }
}
int calc(){
    int r;
    for(r=0;r<mx;r++)if(sz[r]<Q)break;
    for(int i=r*Q;i<=n;i++)if(num[i]==0)return i;
    return n;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&c[i]),pre[i]=c[i];
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        insert(u,v);insert(v,u);
    }
    B=(int)(pow(n,2.0/3)*0.5)+1;
    Q=(int)(sqrt(n))+1;
    for(int i=0;i<=n;i++)be[i]=i/Q;mx=n/Q;
    dfs(1,0);
    while(top)belong[st[top--]]=cnt;
    for(int i=1;i<=m;i++){
        int s,u,v;
        scanf("%d%d%d",&s,&u,&v);
        if(!s)a[++c0]=(mo){u,v,pre[u]},pre[u]=v;
        else{
            if(belong[u]>belong[v])swap(u,v); 
            b[++c1]=(que){u,v,c0,c1};
        } 
    }
    sort(b+1,b+c1+1,cmp);
    for(int i=1;i<=b[1].t;i++)modify(a[i].x,a[i].y);
    solve(b[1].x,b[1].y);
    int L=lca(b[1].x,b[1].y);
    rev(L);ANS[b[1].id]=calc();rev(L);
    for(int i=2;i<=c1;i++){///
        for(int j=b[i-1].t+1;j<=b[i].t;j++)modify(a[j].x,a[j].y);
        for(int j=b[i-1].t;j>b[i].t;j--)modify(a[j].x,a[j].pre);
        solve(b[i-1].x,b[i].x);solve(b[i-1].y,b[i].y);
        int L=lca(b[i].x,b[i].y);
        rev(L);ANS[b[i].id]=calc();rev(L);
    }
    for(int i=1;i<=c1;i++)printf("%d\n",ANS[i]);
    return 0;
}
View Code

 

记得询问先处理1,然后从2开始枚举。

posted @ 2018-03-16 13:35  ONION_CYC  阅读(270)  评论(0编辑  收藏  举报