codeforces E. Tourists (树链剖分+tarjan)

E. Tourists
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

There are n cities in Cyberland, numbered from 1 to n, connected by m bidirectional roads. The j-th road connects city aj and bj.

For tourists, souvenirs are sold in every city of Cyberland. In particular, city i sell it at a price of wi.

Now there are q queries for you to handle. There are two types of queries:

  • "a w": The price in city a is changed to w.
  • "a b": Now a tourist will travel from city a to b. He will choose a route, he also doesn't want to visit a city twice. He will buy souvenirs at the city where the souvenirs are the cheapest (possibly exactly at city a or b). You should output the minimum possible price that he can buy the souvenirs during his travel.

More formally, we can define routes as follow:

  • A route is a sequence of cities [x1, x2, ..., xk], where k is a certain positive integer.
  • For any 1 ≤ i < j ≤ k, xi ≠ xj.
  • For any 1 ≤ i < k, there is a road connecting xi and xi + 1.
  • The minimum price of the route is min(wx1, wx2, ..., wxk).
  • The required answer is the minimum value of the minimum prices of all valid routes from a to b.
Input

The first line of input contains three integers n, m, q (1 ≤ n, m, q ≤ 105), separated by a single space.

Next n lines contain integers wi (1 ≤ wi ≤ 109).

Next m lines contain pairs of space-separated integers aj and bj (1 ≤ aj, bj ≤ n, aj ≠ bj).

It is guaranteed that there is at most one road connecting the same pair of cities. There is always at least one valid route between any two cities.

Next q lines each describe a query. The format is "a w" or "a b" (1 ≤ a, b ≤ n, 1 ≤ w ≤ 109).

Output

For each query of type "A", output the corresponding answer.

Examples
input
3 3 3
1
2
3
1 2
2 3
1 3
A 2 3
C 1 5
A 2 3
output
1
2
input
7 9 4
1
2
3
4
5
6
7
1 2
2 5
1 5
2 3
3 4
2 4
5 6
6 7
5 7
A 2 3
A 6 4
A 6 7
A 3 3
output
2
1
5
3
Note

For the second sample, an optimal routes are:

From 2 to 3 it is [2, 3].

From 6 to 4 it is [6, 5, 1, 2, 4].

From 6 to 7 it is [6, 5, 7].

From 3 to 3 it is [3].


题目大意:给出一个无向简单图,保证任意顶点之间是连通的。每个点有一个点权。

操作分为两种,一种是更改点的点权;另一种是给出起点和终点,求从起点到终点的路径中可能经过的最小点权。

题解:树链剖分+tarjan

给出点双连通分量的定义:对于一个连通图,如果任意两点至少存在两条点不重复路径,则称这个图为点双连通的(简称双连通)。那么同属一个点双连通分量的点,到达其中的一个点就一定能到达连通分量中的所有点。但是我们无法直接进行缩点,因为有些割点会属于很多的点双连通图分量。所以我们对于每个点双连通分量新建一个节点,将所有属于他的点都与他连边,然后我们会得到一颗树。这颗树有不错的性质,首先一定是原图的点与新建节点交替分布的(即不会存在两个原图的点直接相连)。所有非叶子节点的原图的点对应的都是原图的割点。这样我们就得到了一棵树,是所有的点都与他所属的点双相连,并且没有冲突。

那么我们就可以在这棵树上搞事情了。

对于点双的权值,我们定义为他所有儿子节点中的最小点权,这个需要用multiset来维护。

修改一个点的点权也需要对应的修改,他所属的点双(父节点)。

对于查询,因为我们只要经过点双中的一个点就可以获取点双中的最小权值。所以我们对树进行树链剖分,然后查询路径上的最小值即可。需要注意的是如果两个点的lca是新建的点双节点那么我们还要加入fa[lca]的权值,fa[lca]也是属于lca这点双的,只不过是我们没有维护进去。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
#define N 500003
#define inf 1000000000
using namespace std;
int n,m,q,tot,cnt,sz,top,root,n1;
int belong[N],st[N],low[N],dfsn[N],bl[N],head[N],nxt[N],next[N],point[N],val[N];
int u[N],v[N],ins[N],vis[N],mark[N],tr[N*4],pos[N],p[N],fa[N],deep[N],size[N],son[N];
int bk[N];
multiset<int> c[N];
void add(int x,int y)
{
	tot++; next[tot]=head[x]; head[x]=tot; u[tot]=y;
	tot++; next[tot]=head[y]; head[y]=tot; u[tot]=x;
}
void addedge(int x,int y)
{
	tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
	tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void tarjan(int x)
{
	st[++top]=x; low[x]=dfsn[x]=++sz; ins[x]=1;
	for (int i=head[x];i;i=next[i]) {
		int j=u[i];
		if (!dfsn[j]) {
			tarjan(j);
			low[x]=min(low[x],low[j]);
			if (dfsn[x]<=low[j]) {
				++cnt; mark[x]=1; int k;
				while (1){
					k=st[top--];
					bl[k]=cnt;
					addedge(cnt+n,k);
					if (k==j) break;
				}
				addedge(cnt+n,x);
				root=x; 
			}
		}
		else if (ins[j]) low[x]=min(low[x],dfsn[j]);
	}
}
void search(int x)
{
	addedge(cnt+n,x); vis[x]=cnt; bl[x]=cnt;
	for (int i=head[x];i;i=next[i]) {
		if (mark[u[i]]&&vis[u[i]]!=cnt) {
			vis[u[i]]=cnt; addedge(cnt+n,u[i]);
			continue;
		}
		if (vis[u[i]]) continue;
		search(u[i]);
	}
}
void dfs(int x,int f)
{
	fa[x]=f; size[x]=1; deep[x]=deep[f]+1;
	for (int i=point[x];i;i=nxt[i]) {
		if (v[i]==f) continue;
		dfs(v[i],x);
		size[x]+=size[v[i]];
		if (size[son[x]]<size[v[i]]) son[x]=v[i];
	}
}
void dfs1(int x,int chain)
{
	belong[x]=chain; pos[x]=++sz; p[sz]=x;
	if (!son[x]) return;
	dfs1(son[x],chain);
	for (int i=point[x];i;i=nxt[i])
	 if (v[i]!=fa[x]&&v[i]!=son[x])
	  dfs1(v[i],v[i]);
}
void update(int now)
{
	tr[now]=min(tr[now<<1],tr[now<<1|1]);
}
void build(int now,int l,int r)
{
	if (l==r) {
		tr[now]=val[p[l]];
		return;
	}
	int mid=(l+r)/2;
	build(now<<1,l,mid);
	build(now<<1|1,mid+1,r);
	update(now);
}
void pointchange(int now,int l,int r,int x,int v)
{
	if (l==r) {
		tr[now]=v;
		return;
	}
	int mid=(l+r)/2;
	if (x<=mid) pointchange(now<<1,l,mid,x,v);
	else pointchange(now<<1|1,mid+1,r,x,v);
	update(now);
}
int find(int now,int l,int r,int ll,int rr)
{
	if (ll<=l&&r<=rr) return tr[now];
	int mid=(l+r)/2; int ans=inf;
	if (ll<=mid) ans=min(ans,find(now<<1,l,mid,ll,rr));
	if (rr>mid) ans=min(ans,find(now<<1|1,mid+1,r,ll,rr));
	return ans;
}
int solve(int x,int y)
{
	int ans=inf;
	while (belong[x]!=belong[y]) {
		if (deep[belong[x]]<deep[belong[y]]) swap(x,y);
		ans=min(ans,find(1,1,n,pos[belong[x]],pos[x]));
		x=fa[belong[x]];
	}
	if (deep[x]>deep[y]) swap(x,y);
	ans=min(ans,find(1,1,n,pos[x],pos[y]));
	if (x>n1&&fa[x]) ans=min(ans,find(1,1,n,pos[fa[x]],pos[fa[x]]));
	return ans;
}
int main()
{
	freopen("tourists.in","r",stdin);
	freopen("tourists.out","w",stdout);
	scanf("%d%d%d",&n,&m,&q);
	for (int i=1;i<=n;i++) scanf("%d",&val[i]);
	for (int i=1;i<=m;i++) {
		int x,y; scanf("%d%d",&x,&y);
		add(x,y);
	}
	for (int i=1;i<=2*n;i++) bk[i]=i;
	tot=0;
	for (int i=1;i<=n;i++)
	 if (!dfsn[i]) tarjan(i);
	if (cnt==1) root=cnt+n;
	dfs(root,0);
	sz=0; dfs1(root,root);
	for (int i=n+1;i<=n+cnt;i++) val[i]=inf;
	for (int i=1;i<=n;i++) {
		if (i==root) continue;
	    bl[i]=fa[i]; int t=bl[i];
		c[t].insert(val[i]);
		val[t]=min(val[t],*c[t].begin());
	}
	n1=n; n+=cnt; 
	build(1,1,n);
	for (int i=1;i<=q;i++) {
		char opt[10]; int x,y; scanf("%s%d%d",opt,&x,&y);
		if (opt[0]=='A') {
			printf("%d\n",solve(x,y));
		}
		else {
			pointchange(1,1,n,pos[x],y); 
			if (x==root) {
				val[x]=y;
				continue;
			}
			int t=bl[x];
			c[t].erase(c[t].find(val[x])); val[x]=y;
			c[t].insert(val[x]); 
			val[t]=*c[t].begin();
			pointchange(1,1,n,pos[t],val[t]);
		}
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值