其他分享
首页 > 其他分享> > 【题解】CF487E Tourists

【题解】CF487E Tourists

作者:互联网

CF487E Tourists

写完这题已经完全自闭了 调了好久……

题目大意

就是求一张图中两点间所有路径中经过的点的最小值,带修。

解法

我们先考虑一下性质:对于无向图显然不好做,考虑一下咋转化成一棵树。

那就往圆方树考虑呗,本题有啥性质?

观察到:

对于一个点双,必然存在一条路径走过该点双中的最小值。

证明比较简单。由于点双内两点之间没有一个必经点,所以我们必定可以留出一个点从走过的那个最小值点再走出去。

那么就考虑建立圆方树叭。码套路,先上一个 tarjan 找点双……

细节1 注意点双的形成是需要满足 low[j]>=dfn[x] 的,不要犯傻打错了。

好的,接下来怎么修改?考虑回答询问的同时其实也就能想到怎么维护修改:扔一个树剖就好了。

那就考虑写树剖叭。码套路,先上两个 dfs ……

细节2 注意树剖的 query 啥的别打错了……

好嘞,上述准备工作都做好了,写个修改测样例……挂掉了。

冷静分析:发现不论改方点还是圆点,都会影响到周围的点。

怎么办,暴力修改?那显然不切实际,一个菊花图就可以卡爆你。

继续考虑如何高效维护。我们知道,一个方点代表一个点双,那么,对于圆方树的树结构,每一个方点的答案其实除了它的孩子还要包含其父亲。这点也就恰好让维护变得不方便起来。

那么我们不妨先考虑无视掉父亲的影响,只维护孩子,怎么做?

维护一个 multiset 对每个方点,每次直接拿这个来暴力更新方点权值。

好的,现在这里可以实现了,那带上父亲呢?

冷静分析一波,这个父亲的影响需要满足两个条件:

那同时满足下列两个条件的点就只有可能是……

最近公共祖先 \((LCA)\)

所以对于每一组询问实际只有一个点需要被特殊对待!

那么就直接做就好了。

最后再附赠细节3:

更新 low[x] 不要拿 low[j] 啊……(在不 dfs 的时候),调了好久……

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=200100;
int head[N],tot,Head[N],tto,n,m,q,w[N];
struct E {
	int nxt,to;
} e[N<<1],edge[N<<1];
inline int read(){
	int s=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch)){
		s=s*10-'0'+ch;
		ch=getchar();
	} 
	return s;
}
int nodepos,ddf,rk[N],cir[N],To[N];
inline int Min(int x,int y) {return x<y?x:y;}
multiset<int> S[N];
inline void link(int x,int y,int w=0) {
	if(w) {
		edge[++tto]=(E) {
			Head[x],y
		};
		Head[x]=tto;
		return;
	}
	e[++tot]=(E) {
		head[x],y
	};
	head[x]=tot;
}
int son[N],siz[N],dfn[N],low[N],st[N],top;
int ttop[N],pa[N],dfstime,vis[N],dep[N];
void dfs1(int x,int fa) {
	siz[x]=1;
	dep[x]=dep[fa]+1;
	pa[x]=fa;
	for(int i=Head[x]; i; i=edge[i].nxt) {
		int j=edge[i].to;
		if(j==fa)continue;
		dfs1(j,x);
		siz[x]+=siz[j];
		if(siz[j]>siz[son[x]])son[x]=j;
	}
}
void dfs2(int x,int t) {
	rk[dfn[x]=++ddf]=x;
	ttop[x]=t;
	if(!son[x])return;
	dfs2(son[x],t);
	for(int i=Head[x]; i; i=edge[i].nxt) {
		int j=edge[i].to;
		if(j==son[x]||j==pa[x])continue;
		dfs2(j,j);
	}
}
void tarjan(int x,int root) {
	dfn[x]=low[x]=++dfstime;
	st[++top]=x;
	if(x==root&&!head[x]) {
		++nodepos;
		To[nodepos]=x;
		cir[x]=nodepos;
		link(x,nodepos,1);
		link(nodepos,x,1);
		return;
	}
	int ch=0;
	for(int i=head[x]; i; i=e[i].nxt) {
		int j=e[i].to;
		if(!dfn[j]) {
			++ch;
			tarjan(j,root);
			low[x]=Min(low[x],low[j]);
			if(dfn[x]<=low[j]&&x!=root)vis[x]=1;
			if(x==root&&ch>1)vis[x]=1;
			if(low[j]>=dfn[x]) {
				int vex=-1;
				++nodepos;
				To[nodepos]=x;
				do {
					vex=st[top--];
					cir[vex]=nodepos;
					link(vex,nodepos,1);
					link(nodepos,vex,1);
				} while(vex!=j);
				cir[x]=nodepos;
				link(nodepos,x,1);
				link(x,nodepos,1);
			}
		} else low[x]=Min(low[x],dfn[j]);
	}
}
namespace SGT {
	int mi[N<<2],ls[N<<2],rs[N<<2],node;
	inline void pushup(int x) {
		mi[x]=Min(mi[ls[x]],mi[rs[x]]);
	}
	void build(int &x,int l,int r) {
		x=++node;
		if(l==r) {
			mi[x]=w[rk[l]];
			return;
		}
		int mid=(l+r)>>1;
		build(ls[x],l,mid);
		build(rs[x],mid+1,r);
		pushup(x);
	}
	void change(int x,int L,int R,int pos,int v) {
		if(L==R) {
			mi[x]=v;
			return;
		}
		int mid=(L+R)>>1;
		if(pos<=mid)change(ls[x],L,mid,pos,v);
		else change(rs[x],mid+1,R,pos,v);
		pushup(x);
	}
	int query(int x,int L,int R,int l,int r) {
		if(L>=l&&R<=r){return mi[x];} 
		int mid=(L+R)>>1,res=(1LL<<60);
		if(l<=mid)res=query(ls[x],L,mid,l,r);
		if(mid<r)res=Min(res,query(rs[x],mid+1,R,l,r));
		return res;
	}
}
using namespace SGT;
int rt;
int Query(int u,int v) {
	int res=(1LL<<60);
	while(ttop[u]!=ttop[v]) {
		if(dep[ttop[u]]<dep[ttop[v]])swap(u,v);
		res=Min(res,query(rt,1,nodepos,dfn[ttop[u]],dfn[u]));
		u=pa[ttop[u]];
	}
	if(dep[u]<dep[v])swap(u,v);
	res=Min(res,query(rt,1,nodepos,dfn[v],dfn[u]));
	if(dep[u]>dep[v])swap(u,v);
	if(u>n)res=Min(res,w[pa[u]]);
	return res;
}
void Change(int pos,int v) {
	change(rt,1,nodepos,dfn[pos],v);
	if(pos==1) {
		w[pos]=v;
		return;
	}
	int o=pa[pos];
	S[o-n].erase(S[o-n].find(w[pos]));
	S[o-n].insert(v);
	int minv=*S[o-n].begin();
	if(minv==w[o]){
		w[pos]=v;
		return;
	}
	change(rt,1,nodepos,dfn[o],minv);
	w[o]=minv;
	w[pos]=v;
}
inline void write(int x){
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
signed main() {
	freopen("in.txt","r",stdin);
	n=read();m=read();q=read();
	for(int i=1; i<=n; ++i)w[i]=read();
	for(int i=1; i<=m; ++i) {
		int u=read(),v=read();
		link(u,v);
		link(v,u);
	}
	nodepos=n;
	tarjan(1,1);
	memset(dfn,0,sizeof dfn);
	dfs1(1,0);
	dfs2(1,1);
	for(int i=2; i<=n; ++i) {
		S[pa[i]-n].insert(w[i]);
	}
	for(int i=n+1; i<=nodepos; ++i)
		w[i]=(S[i-n].empty()?(1LL<<60):(*S[i-n].begin()));
	memset(mi,0x7f,sizeof mi);
	build(rt,1,nodepos);
	while(q--) {
		int u,v;
		char opt;
		cin>>opt;
		u=read();v=read();
		if(opt=='A') {
			write(Query(u,v));
			putchar('\n');
			continue;
		}
		Change(u,v);
	}
	return 0;
}

标签:nodepos,int,题解,pos,++,dfn,low,CF487E,Tourists
来源: https://www.cnblogs.com/h-lka/p/15315107.html