Codeforces 487E Tourists

11 篇文章 0 订阅
2 篇文章 0 订阅

题意:给出一幅图和每个点的权值,每次修改某个点权值或询问两点间所有简单路径上的点权值最小值

思路:

只能看出来tarjan缩点和树剖,然后完全未考虑清楚细节。。似乎建图方法很高级

首先tarjan缩点,对于每个块,新建一个点x,x向所有块内不是深度最小的点连边(借鉴(抄袭)某神犇的方法),然后深度最小的点向x连边,然后x的权值为所有块内不是深度最小的点的权值的最小值,然后询问时如果lca是新建点(序号>n)那就还要跟其父亲节点权值取min,修改时也要修改父亲节点(必定为新建点)的值,然后每个新建点可以用multiset(第一次用,好高级)维护块内(除掉某个点)的最小值。。

注意缩点姿势比较神奇(深度最小的点不要退栈啊!)

代码:

#include <bits/stdc++.h>
#define gc getchar()
#define N 200009
#define M 200009
#define mid (l+r>>1)
#define root 1,1,cnt
#define lson cur<<1,l,mid
#define rson cur<<1|1,mid+1,r
#define lc cur<<1
#define rc cur<<1|1
using namespace std;
typedef multiset<int>::iterator ptr;
int n,m,Q,x,y,first[N],number=1;
int Number,First[N<<1],rt;
int top,cnt,taj,vis[N<<1],size[N<<1];
int num[N<<1],deep[N<<1],Top[N<<1],father[N<<1],Mson[N<<1];
int dfn[N<<1],low[N],sta[M],val[N],Id[N];
int v[N<<3];
multiset<int> q[N<<3];
struct edge
{
	int from,to,next;
	void add(int x,int y)
	{
		from=x,to=y,next=first[x],first[x]=number;
	}
}e[N<<1];
struct Edge
{
	int to,next;
	void add(int x,int y)
	{
		to=y,next=First[x],First[x]=Number;
	}
}E[N<<3];
int read()
{
	int x=1;
	char ch;
	while (ch=gc,ch<'0'||ch>'9') if (ch=='-') x=-1;
	int s=ch-48;
	while (ch=gc,ch>='0'&&ch<='9') s=s*10+ch-48;
	return x*s;
}
void tarjan(int x)
{
	dfn[x]=low[x]=++cnt;
	sta[++top]=x;
	for (int i=first[x];i;i=e[i].next)
		if (!dfn[e[i].to])
		{
			tarjan(e[i].to);
			low[x]=min(low[x],low[e[i].to]);
			if (low[e[i].to]>=dfn[x])
			{
				++taj;
				father[taj]=x;
				E[++Number].add(x,taj);
				do
				{
					y=sta[top--];
					father[y]=taj;
					E[++Number].add(taj,y);
					q[taj].insert(val[y]);
				}while (y!=e[i].to);
			}
		}
		else low[x]=min(low[x],dfn[e[i].to]);
}
void dfs1(int x)
{
	size[x]=1;
	deep[x]=deep[father[x]]+1;
	for (int i=First[x];i;i=E[i].next)
	{
		dfs1(E[i].to);
		size[x]+=size[E[i].to];
		if (size[E[i].to]>size[Mson[x]]) Mson[x]=E[i].to;
	}
}
void dfs2(int x,int y)
{
	Top[x]=y;
	dfn[x]=++cnt;
	Id[cnt]=x;
	if (Mson[x]) dfs2(Mson[x],y);
	for (int i=First[x];i;i=E[i].next)
		if (E[i].to!=Mson[x]) dfs2(E[i].to,E[i].to);
}
void build(int cur,int l,int r)
{
	if (l==r)
	{
		v[cur]=val[Id[l]];
		return;
	}
	build(lson);
	build(rson);
	v[cur]=min(v[lc],v[rc]);
}
void ins(int cur,int l,int r,int x,int y)
{
	if (l==r)
	{
		v[cur]=y;
		return;
	}
	if (x<=mid) ins(lson,x,y);
	else ins(rson,x,y);
	v[cur]=min(v[lc],v[rc]);
}
int qry(int cur,int l,int r,int L,int R)
{
	if (L<=l&&R>=r) return v[cur];
	int ans=1000000000;
	if (L<=mid) ans=min(ans,qry(lson,L,R));
	if (R>mid) ans=min(ans,qry(rson,L,R));
	return ans;
}
int qry(int x,int y)
{
	int ans=1000000000;
	for (;Top[x]!=Top[y];x=father[Top[x]])
	{
		if (deep[Top[x]]<deep[Top[y]]) swap(x,y);
		ans=min(ans,qry(root,dfn[Top[x]],dfn[x]));
	}
	if (dfn[x]>dfn[y]) swap(x,y);
	ans=min(ans,qry(root,dfn[x],dfn[y]));
	int lca=(dfn[x]<dfn[y])?x:y;
	if (lca>n) ans=min(ans,val[father[lca]]);
	return ans;
}
int getroot(int x)
{
	return father[x]?getroot(father[x]):x;
}
int main()
{
	memset(First,0,sizeof(First));
	n=read(),m=read(),Q=read();
	for (int i=1;i<=n;i++) val[i]=read();
	for (int i=1;i<=m;i++)
	{
		x=read(),y=read();
		e[++number].add(x,y),e[++number].add(y,x);
	}
	taj=n;
	tarjan(1);
	memset(dfn,0,sizeof(dfn));
	cnt=0;
	rt=getroot(1);
	dfs1(rt);
	dfs2(rt,rt);
	for (int i=n+1;i<=taj;i++)
		val[i]=*q[i].begin();
	build(root);
	for (int i=1;i<=Q;i++)
	{
		char ch;
		while (ch=gc,ch!='A'&&ch!='C');
		x=read(),y=read();
		if (ch=='C')
		{
			if (father[x])
				q[father[x]].erase(q[father[x]].find(val[x]));
			ins(root,dfn[x],y);
			val[x]=y;
			if (father[x])
			{
				q[father[x]].insert(y);
				val[father[x]]=*q[father[x]].begin();
				ins(root,dfn[father[x]],val[father[x]]);
			}
		}
		else printf("%d\n",qry(x,y));
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值