zoukankan      html  css  js  c++  java
  • 【UOJ #30】【CF Round #278】Tourists【Tarjan算法+树链剖分】

    【CF Round #278】Tourists


    Cyberland 有 nn 座城市,编号从 11 到 nn,有 mm 条双向道路连接这些城市。第 jj 条路连接城市 ajaj 和 bjbj。每天,都有成千上万的游客来到 Cyberland 游玩。

    在每一个城市,都有纪念品售卖,第 ii 个城市售价为 wiwi。这个售价有时会变动。

    每一个游客的游览路径都有固定起始城市和终止城市,且不会经过重复的城市。

    他们会在路径上的城市中,售价最低的那个城市购买纪念品。

    你能求出每一个游客在所有合法的路径中能购买的最低售价是多少吗?

    你要处理 qq 个操作:

    • C a w: 表示 aa 城市的纪念品售价变成 ww
    • A a b: 表示有一个游客要从 aa 城市到 bb 城市,你要回答在所有他的旅行路径中最低售价的最低可能值。

    输入格式

    第一行包含用一个空格隔开的三个数,nnmmqq

    接下来 nn 行,每行包含一个数 wiwi

    接下来 mm 行,每行包含用一个空格隔开的两个数 ajajbjbj。(1aj,bjn,ajbj1≤aj,bj≤n,aj≠bj

    数据保证没有两条道路连接同样一对城市,也没有一条道路两端是相同的城市。并且任意两个城市都可以相互到达。

    接下来 qq 行,每行是C a w 或 A a b ,描述了一个操作。

    输出格式

    对于每一个A类操作,输出一行表示对应的答案。

    题意:一个无向图,每个点有一个点权。维护两种操作,修改一个点权,或者查询所有两点之间所有路径中不重复经过一个点的路径经过的最小点权的最小值。

    题解:参考http://blog.csdn.net/FromATP/article/details/69389705

    (由于博主过于蒟蒻而无法证明)

    首先tarjan算法求出点双联通分量,然后所有的双联通分量建一个点,每个原来的点向自己所属的双联通分量所对应的节点连一条边,这样形成的一定是一棵树,且一定是原来的点与新建的点交替出现的。然后每个双联通分量对应的一个点开一棵平衡树维护属于这个强联通分量的最小值,把这个最小值当作它的权值。接着把这棵树树链剖分,每次询问的答案就是树上两点之间路径上的权值的最小值。建树的方法如下图所示。


    但是这样修改一个点就会把它所属的所有双联通分量的维护信息修改,在极限情况下时间是不可承受的。于是我们就只要在在所有原来的点在它的父亲节点对应的连通分量上维护即可。查询时,注意两点之间的LCA是一个双联通分量时,这个LCA的父亲也是属于这个连通分量的点,但我们却只在LCA的父亲的父亲对应的强联通分量上维护了这个点,所以需要特判一下。

    本代码完全自给自足,没有用到任何STL,平衡树是用替罪羊树实现的!

    AC代码

    #include<cstdio>
    using namespace std;
    const int N=100005;
    int n,m,q,u,v,Cnt,idx,tot,clk,w[2*N],Head[N],From[N*2],To[N*2],Nxt[N*2],id[N*2];
    int cnt,head[N*2],to[N*4],nxt[N*4];
    int stk[N*2],dfn[N],low[N];
    int fa[N*2],sz[N*2],son[N*2],top[N*2],dep[N*2],pos[N*2],num[N*2];
    bool vis[N];
    int mmp[50*N],key[50*N],ch[50*N][2],siz[50*N],tt[50*N],pp[50*N];
    bool del[50*N];
    char s[5];
    int max(int a,int b){
    	return a<b?b:a;
    }
    int min(int a,int b){
    	return a<b?a:b;
    }
    struct ScapegoatTree{
    	int root,*goat;
    	int rnk(int x){
    		int k=root,ret=1;
    		while(k){
    			if(x<=key[k]){
    				k=ch[k][0];
    			}else{
    				ret+=siz[ch[k][0]]+del[k];
    				k=ch[k][1];
    			}
    		}
    		return ret;
    	}
    	int kth(int x){
    		int k=root;
    		while(k){
    			if(del[k]&&x==siz[ch[k][0]]+1){
    				return key[k];
    			}else{
    				if(x<=siz[ch[k][0]]+del[k]){
    					k=ch[k][0];
    				}else{
    					x-=siz[ch[k][0]]+del[k];
    					k=ch[k][1];
    				}
    			}
    		}
    		return 0;
    	}
    	void dfs(int k){
    		if(!k){
    			return;
    		}
    		dfs(ch[k][0]);
    		if(del[k]){
    			pp[++pp[0]]=k;
    		}else{
    			mmp[++mmp[0]]=k;
    		}
    		dfs(ch[k][1]);
    	}
    	void build(int &k,int l,int r){
    		if(l>r){
    			k=0;
    			return;
    		}
    		int mid=(l+r)/2;
    		k=pp[mid];
    		build(ch[k][0],l,mid-1);
    		build(ch[k][1],mid+1,r);
    		siz[k]=siz[ch[k][0]]+siz[ch[k][1]]+1;
    		tt[k]=tt[ch[k][0]]+tt[ch[k][1]]+1;
    	}
    	void rebuild(int &k){
    		pp[0]=0;
    		dfs(k);
    		build(k,1,pp[0]);
    	}
    	void insert(int &k,int x){
    		if(!k){
    			k=mmp[mmp[0]--];
    			siz[k]=tt[k]=del[k]=1;
    			key[k]=x;
    			ch[k][0]=ch[k][1]=0;
    			return;
    		}
    		siz[k]++;
    		tt[k]++;
    		if(x<=key[k]){
    			insert(ch[k][0],x);
    		}else{
    			insert(ch[k][1],x);
    		}
    		if(tt[k]*0.75<max(tt[ch[k][0]],tt[ch[k][1]])){
    			goat=&k;
    		}
    	}
    	void insert(int x){
    		goat=NULL;
    		insert(root,x);
    		if(goat){
    			rebuild(*goat);
    		}
    	}
    	void remove(int &k,int x){
    		if(del[k]&&x==siz[ch[k][0]]+1){
    			siz[k]--;
    			del[k]=0;
    			return;
    		}
    		siz[k]--;
    		if(x<=siz[ch[k][0]]+del[k]){
    			remove(ch[k][0],x);
    		}else{
    			remove(ch[k][1],x-siz[ch[k][0]]-del[k]);
    		}
    	}
    	void remove(int x){
    		remove(root,rnk(x));
    		if(siz[root]<tt[root]*0.75){
    			rebuild(root);
    		}
    	}
    }sgt[N*10],Set[N*2];
    void adde(int u,int v){
    	if(Set[u].kth(Set[u].rnk(v))==v){
    		return;
    	}
    	Set[u].insert(v);
    	to[++cnt]=v;
    	nxt[cnt]=head[u];
    	head[u]=cnt;
    }
    void tarjan(int pre,int u){
    	dfn[u]=low[u]=++idx;
    	int v;
    	for(int i=Head[u];i;i=Nxt[i]){
    		v=To[i];
    		if(v!=pre&&!vis[id[i]]){
    			stk[++stk[0]]=i;
    			vis[id[i]]=true;
    		}
    		if(!dfn[v]){
    			tarjan(u,v);
    			low[u]=min(low[u],low[v]);
    			if(low[v]>=dfn[u]){
    				tot++;
    				int x;
    				do{
    					x=stk[stk[0]];
    					adde(From[x],tot);
    					adde(tot,From[x]);
    					adde(To[x],tot);
    					adde(tot,To[x]);
    				}while(id[i]!=id[stk[stk[0]--]]);
    			}
    		}else{
    			low[u]=min(low[u],dfn[v]);
    		}
    	}
    }
    void dfs(int u){
    	sz[u]=1;
    	int v;
    	for(int i=head[u];i;i=nxt[i]){
    		v=to[i];
    		if(v!=fa[u]){
    			if(v<=n){
    				sgt[u].insert(w[v]);
    			}
    			fa[v]=u;
    			dep[v]=dep[u]+1;
    			dfs(v);
    			sz[u]+=sz[v];
    			if(!son[u]||sz[son[u]]<sz[v]){
    				son[u]=v;
    			}
    		}
    	}
    }
    void dfs(int u,int tp){
    	pos[u]=++clk;
    	num[clk]=u;
    	top[u]=tp;
    	if(son[u]){
    		dfs(son[u],tp);
    	}
    	int v;
    	for(int i=head[u];i;i=nxt[i]){
    		v=to[i];
    		if(v!=fa[u]&&v!=son[u]){
    			dfs(v,v);
    		}
    	}
    }
    struct SegmentTree{
    	int minv[N*10],lc[N*10],rc[N*10];
    	void build(int o,int l,int r){
    		if(l==r){
    			minv[o]=w[num[l]];
    			return;
    		}
    		int mid=(l+r)/2;
    		build(o*2,l,mid);
    		build(o*2+1,mid+1,r);
    		minv[o]=min(minv[o*2],minv[o*2+1]);
    	}
    	void update(int o,int l,int r,int k,int v){
    		if(l==r){
    			minv[o]=v;
    			return;
    		}
    		int mid=(l+r)/2;
    		if(k<=mid){
    			update(o*2,l,mid,k,v);
    		}else{
    			update(o*2+1,mid+1,r,k,v);
    		}
    		minv[o]=min(minv[o*2],minv[o*2+1]);
    	}
    	int query(int o,int l,int r,int L,int R){
    		if(L<=l&&R>=r){
    			return minv[o];
    		}
    		int mid=(l+r)/2,ans=0x7fffffff;
    		if(L<=mid){
    			ans=min(ans,query(o*2,l,mid,L,R));
    		}
    		if(R>mid){
    			ans=min(ans,query(o*2+1,mid+1,r,L,R));
    		}
    		return ans;
    	}
    }st;
    int main(){
    	for(int i=1000000;i>=1;i--){
    		mmp[++mmp[0]]=i;
    	}
    	scanf("%d%d%d",&n,&m,&q);
    	tot=n;
    	for(int i=1;i<=n;i++){
    		scanf("%d",&w[i]);
    	}
    	for(int i=1;i<=m;i++){
    		scanf("%d%d",&u,&v);
    		To[++Cnt]=v;
    		From[Cnt]=u;
    		Nxt[Cnt]=Head[u];
    		Head[u]=Cnt;
    		id[Cnt]=i;
    		To[++Cnt]=u;
    		From[Cnt]=v;
    		Nxt[Cnt]=Head[v];
    		Head[v]=Cnt;
    		id[Cnt]=i;
    	}
    	tarjan(0,1);
    	dfs(1);
    	dfs(1,1);
    	for(int i=n+1;i<=tot;i++){
    		w[i]=sgt[i].kth(1);
    	}
    	st.build(1,1,clk);
    	for(int i=1;i<=q;i++){
    		scanf("%s%d%d",s,&u,&v);
    		if(s[0]=='C'){
    			if(fa[u]){
    				sgt[fa[u]].remove(w[u]);
    				sgt[fa[u]].insert(v);
    				w[fa[u]]=sgt[fa[u]].kth(1);
    				st.update(1,1,clk,pos[fa[u]],w[fa[u]]);
    			}
    			w[u]=v;
    			st.update(1,1,clk,pos[u],w[u]);
    		}else{
    			int ans=0x7fffffff;
    			while(top[u]!=top[v]){
    				if(dep[top[u]]<dep[top[v]]){
    					u^=v;
    					v^=u;
    					u^=v;
    				}
    				ans=min(ans,st.query(1,1,clk,pos[top[u]],pos[u]));
    				u=fa[top[u]];
    			}
    			if(dep[u]>dep[v]){
    				u^=v;
    				v^=u;
    				u^=v;
    			}
    			ans=min(ans,st.query(1,1,clk,pos[u],pos[v]));
    			if(u>n){
    				ans=min(ans,w[fa[u]]);
    			}
    			printf("%d
    ",ans);
    		}
    	}
    	return 0;
    }



  • 相关阅读:
    MFC单文档视图设置背景
    原来这就是命令行下的“学生信息管理系统”
    C语言中数组&取地址的问题
    《逐梦旅程-Windows游戏编程之从零开始》 勘误
    杭电ACM 1003 ( 动态规划法 水题)
    《编程之美》
    [蓝桥杯][2014年第五届真题]地宫取宝
    [蓝桥杯][2013年第四届真题]危险系数
    2142: 逛超市(zznuoj)
    2141:2333(zznuoj)
  • 原文地址:https://www.cnblogs.com/2016gdgzoi471/p/9476916.html
Copyright © 2011-2022 走看看