Loading

CF487E Tourists

题意

给定一张无向图,点有点权。每次可以修改一个点的点权,或者询问从 \(a\)\(b\) 所有不经过重复点的路径上最小的点权是多少。

Solution

考虑一个点双,点双中任意两个点之间都有两条以上的不同路径,换句话说,对于一个点双,我从任意点出发到任何异于起点的点去,总能取到点双中点权最小的点。这提示我们,维护点双是没有意义的。

考虑如何把点双啊掉。不难想到圆方树。建完之后,在方点上记录这个点双中的最小点权,然后每次询问就相当于是询问路径最小值,直接树剖维护就行了。

但是这样直接做似乎能卡到 \(O(n^2)\),因为一个圆点连接的方点有很多,不能一个个改过去。

那怎么办捏。。。\(\bigstar\) 人类智慧!先定根,然后每个方点维护的是它所有儿子的最小值。这样,修改一个圆点的时候,就只需要修改它父亲的最小值就可以了。至于维护最小值,对每个点开一个 multiset 就可以了。

此时需要注意,如果在树剖的时候,LCA 是一个方点的话,其父亲的贡献也要加入。

Code

// Problem: 
//     Tourists
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF487E
// Memory Limit: 250 MB
// Time Limit: 2000 ms

#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
// #define int long long
using namespace std;
const int MAXN=2e5+10;
vector<int> g[MAXN],e[MAXN];
multiset<int> mn[MAXN];
void add(int u,int v){e[u].pb(v);e[v].pb(u);}
int rk[MAXN],top[MAXN];
int w[MAXN],n,nod;

struct Tree{int l,r,mn;}tr[MAXN<<2];
#define ls i<<1
#define rs i<<1|1
void pushup(int i){tr[i].mn=min(tr[ls].mn,tr[rs].mn);}
void build(int i,int l,int r){
	tr[i].l=l;tr[i].r=r;if(l==r){tr[i].mn=w[rk[l]];return;}
	int mid=(l+r)>>1;build(ls,l,mid);build(rs,mid+1,r);pushup(i);
}
void upd(int i,int x,int v){
	if(tr[i].l==tr[i].r){tr[i].mn=v;return;}int mid=(tr[i].l+tr[i].r)>>1;
	if(x<=mid) upd(ls,x,v);else upd(rs,x,v);pushup(i);
}
int ask(int i,int l,int r){
	if(tr[i].l==l&&tr[i].r==r) return tr[i].mn;int mid=(tr[i].l+tr[i].r)>>1;
	if(r<=mid) return ask(ls,l,r);else if(l>mid) return ask(rs,l,r);
	else return min(ask(ls,l,mid),ask(rs,mid+1,r));
}

int dfn[MAXN],low[MAXN],stk[MAXN],tp,tot;
void Tarjan(int x){
	dfn[x]=low[x]=++tot;stk[++tp]=x;
	for(int s:g[x]){
		if(!dfn[s]){
			Tarjan(s);
			low[x]=min(low[x],low[s]);
			if(low[s]>=dfn[x]){
				nod++;add(nod,x);
				do{add(nod,stk[tp]);
				}while(stk[tp--]!=s);
			}
		}else low[x]=min(low[x],dfn[s]);
	}
}

int son[MAXN],siz[MAXN],fa[MAXN],dep[MAXN];
void dfs1(int x,int f){
	siz[x]=1;son[x]=-1;
	for(int s:e[x]){
		if(s==f) continue;
		if(x>n) mn[x].insert(w[s]);
		dep[s]=dep[x]+1;fa[s]=x;
		dfs1(s,x);siz[x]+=siz[s];
		if(son[x]==-1||siz[son[x]]<siz[s])
			son[x]=s;
	}if(x>n) w[x]=*mn[x].begin();
}
void dfs2(int x,int t){
	dfn[x]=++tot;rk[tot]=x;
	top[x]=t;
	if(~son[x]) dfs2(son[x],t);
	for(int s:e[x])
		if(s!=son[x]&&s!=fa[x])
			dfs2(s,s);
}

signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int m,Q;
	cin>>n>>m>>Q;
	rep(i,1,n) cin>>w[i];
	rep(i,1,m){
		int u,v;cin>>u>>v;
		g[u].pb(v);g[v].pb(u);
	}nod=n;
	Tarjan(1);
	memset(dfn,0,sizeof(dfn));tot=0;
	dfs1(1,0);dfs2(1,1);
	build(1,1,nod);
	char op;int a,b;
	while(Q--){
		cin>>op>>a>>b;
		if(op=='C'){
			upd(1,dfn[a],b);
			int f=fa[a];
			if(f){
				mn[f].erase(mn[f].find(w[a]));
				mn[f].insert(b);w[f]=*mn[f].begin();
				upd(1,dfn[f],w[f]);
			}w[a]=b;
		}else{
			int ans=inf;
			while(top[a]^top[b]){
				if(dep[top[a]]<dep[top[b]]) swap(a,b);
				ans=min(ans,ask(1,dfn[top[a]],dfn[a]));
				a=fa[top[a]];
			}if(dfn[a]>dfn[b]) swap(a,b);
			ans=min(ans,ask(1,dfn[a],dfn[b]));
			if(a>n) ans=min(ans,w[fa[a]]);
			cout<<ans<<'\n';
		}
	}
	return 0;
}
posted @ 2022-11-08 08:22  ZCETHAN  阅读(22)  评论(0编辑  收藏  举报