• 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;
    }
    
    
  • 相关阅读:
    vi里面全局替换
    guanbi selinux
    ntop
    Java:求一个数组中连续子元素最大和
    LeetCode.643. 子数组最大平均数 I
    分治法-最大子数组问题
    Java实现最大连续子数组和
    golang xorm cmd xorm工具使用 reverse 反转一个数据库结构,生成代码
    golang中xorm的基本使用
    xorm入门
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8745182.html
Copyright © 2020-2023  润新知