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
",ans[i]);
	return 0;
}

原文地址:https://www.cnblogs.com/Yuzao/p/8745182.html