bzoj 4129: Haruna’s Breakfast
Description
Haruna每天都会给提督做早餐! 这天她发现早饭的食材被调皮的 Shimakaze放到了一棵
树上,每个结点都有一样食材,Shimakaze要考验一下她。
每个食材都有一个美味度,Shimakaze会进行两种操作:
1、修改某个结点的食材的美味度。
2、对于某条链,询问这条链的美味度集合中,最小的未出现的自然数是多少。即mex值。
请你帮帮Haruna吧。
Solution
树上莫队
按照老方法分块,块大小为 \(n^{\frac{2}{3}}\)
然后对于求 \(mex\) 的方法,我不晓得怎么写,于是就写了一个指针移动带 \(log\) 的方法
树状数组维护 \(mex\) 数,我们二分到最后一个满足 \(sum[i]=i+1\) 的位置, \(sum[i]\) 表示小于 \(i\) 的数出现的总次数(出现多次算一次),答案就是 \(i+1\),用树状数组维护 \(sum\) 即可
注意一些优化:大于 \(n\) 的值是不可能成为 \(mex\) 的,可以直接去掉
复杂度 \(O(n^{\frac{5}{3}}*logn)\)
树剖写挂T飞了,调了好久
关于树上莫队,还有一些个人的理解:
序列上的莫队之所以可以直接移动指针,是因为是有序的(假设 \(l\) 指针小于等于 \(i\), 也就是说 \(i+1\) 被算进去了 \(i\) 一定也被算进去了)
而树上就不一定满足(一个点可以被多次算入贡献),所以我们把改为一个存在性问题,给每一个点一个 \(vis\) 标记,访问第一次加入,访问第二次就删除,这样就好了
这正好满足异或运算的规律,所以转移过程可以用异或运算来推导,利用异或运算的交换率和结合率就可以实现序列莫队的复杂度了
#include<bits/stdc++.h>
#define RG register
using namespace std;
const int N=50010;
int n,a[N],Q,head[N],nxt[N*2],to[N*2],num=0,fa[N],B,st[N],top=0;
int b[N],v[N],cnt=0,sz[N],dep[N],Top[N],son[N],siz[N],tp;
inline void link(int x,int y){
nxt[++num]=head[x];to[num]=y;head[x]=num;
nxt[++num]=head[y];to[num]=x;head[y]=num;
}
inline void dfs(int x){
siz[x]=1;
for(int u,i=head[x];i;i=nxt[i]){
if((u=to[i])==fa[x])continue;
fa[u]=x;dep[u]=dep[x]+1;dfs(u);
sz[x]+=sz[u];siz[x]+=siz[u];
if(sz[x]>=B){
cnt++;
while(sz[x])b[st[top--]]=cnt,sz[x]--;
}
if(siz[u]>siz[son[x]])son[x]=u;
}
st[++top]=x;sz[x]++;
}
inline void dfs2(int x,int tp){
Top[x]=tp;
if(son[x])dfs2(son[x],tp);
for(int i=head[x];i;i=nxt[i])
if(to[i]!=son[x] && to[i]!=fa[x])dfs2(to[i],to[i]);
}
struct data{
int x,y,t,id;
bool operator <(const data &p)const{
if(b[x]!=b[p.x])return b[x]<b[p.x];
if(b[y]!=b[p.y])return b[y]<b[p.y];
return t<p.t;
}
}S[N],T[N];
bool vis[N];int t[N],tr[N],ans[N],pre[N];
inline void add(int x,int t){
for(RG int i=x;i<=n;i+=(i&(-i)))tr[i]+=t;
}
inline int qry(int x){
int ret=0;
for(RG int i=x;i>=1;i-=(i&(-i)))ret+=tr[i];
return ret;
}
inline void rev(int x){
if(a[x]>n)return ;
vis[x]^=1;
if(vis[x]){t[a[x]]++;if(t[a[x]]==1)add(a[x],1);}
else{t[a[x]]--;if(t[a[x]]==0)add(a[x],-1);}
}
inline void upd(int x,int y){
while(x!=y){
if(dep[x]<dep[y])swap(x,y);
rev(x);x=fa[x];
}
}
inline void change(int x,int y){
if(vis[x])rev(x),a[x]=y,rev(x);
else a[x]=y;
}
inline int LCA(int x,int y){
while(Top[x]!=Top[y]){
if(dep[Top[x]]<dep[Top[y]])swap(x,y);
x=fa[Top[x]];
}
return dep[x]<dep[y]?x:y;
}
inline int getans(){
int l=1,r=n,mid,ret=0;
while(l<=r){
mid=(l+r)>>1;
if(qry(mid)>v[mid])ret=mid,l=mid+1;
else r=mid-1;
}
return v[ret]+1;
}
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
int x,y,op;
cin>>n>>Q;
for(B=1;1ll*B*B*B<=1ll*n*n;B++);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),v[i]=i-1;
v[0]=-1;
for(int i=1;i<=n;i++)pre[i]=a[i]=lower_bound(v+1,v+n+1,a[i])-v;
for(int i=1;i<n;i++)scanf("%d%d",&x,&y),link(x,y);
dfs(1);dfs2(1,1);
if(top){
cnt++;
while(top)b[st[top--]]=cnt;
}
int p=0,q=0,t=0;
for(int i=1;i<=Q;i++){
scanf("%d%d%d",&op,&x,&y);
if(op==0)y=lower_bound(v+1,v+n+1,y)-v;
if(op==0)S[++p]=(data){x,y,p,pre[x]},pre[x]=y;
else {
if(b[x]>b[y])swap(x,y);
T[++q]=(data){x,y,p,q};
}
}
if(!q)exit(0);
sort(T+1,T+q+1);
while(t<T[1].t)t++,change(S[t].x,S[t].y);
upd(T[1].x,T[1].y);
int lca=LCA(T[1].x,T[1].y);rev(lca);
ans[T[1].id]=getans();rev(lca);
for(int i=2;i<=q;i++){
while(t<T[i].t)t++,change(S[t].x,S[t].y);
while(t>T[i].t)change(S[t].x,S[t].id),t--;
upd(T[i-1].x,T[i].x);
upd(T[i-1].y,T[i].y);
lca=LCA(T[i].x,T[i].y);rev(lca);
ans[T[i].id]=getans();rev(lca);
}
for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
return 0;
}