【题意】给定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 ",ANS[i]); return 0; }
记得询问先处理1,然后从2开始枚举。