UOJ #30. 【CF Round #278】Tourists Tarjan+树链剖分

14 篇文章 0 订阅
13 篇文章 0 订阅
这篇博客介绍了一个UOJ上的问题,要求求解无向连通图中两点间的简单路径上最小点权值的最小值。通过使用Tarjan算法求解点双联通分量,并结合树链剖分,博主解释了如何找到路径上的最小点权值。关键在于处理割点时,用虚拟节点连接割点与其所在的所有点双,并以割点的虚拟节点父亲维护权值。在更新点权和寻找最近公共祖先(LCA)时,优化了时间复杂度,避免了O(n^2)的情况。最终,博主实现了高效的解决方案,代码长度仅为4KB,且在比赛中取得了第一的好成绩。
摘要由CSDN通过智能技术生成

题目链接:http://uoj.ac/problem/30

题目大意:给定一张无向连通图,每个点有点权,多次询问两个点之间的简单路径上最小点权值的最小值

首先求的是最小点权值的最小值 那么我们肯定要走点权越小的点越好 但是由于求的是简单路径 那么我们就不能经过同一个点两次

那么我们Tarjan求点双联通分量 每个点双维护一个最小值 那么答案显然就是两点所在点双路径上的最小值 树链剖分即可 (正确性我不会证

但是一个割点可以属于多个点双 因此割点在计算距离的时候要取最近的那个点双 建图时对于每一个割点建一个权值为+∞的虚点连向所有所在的点双即可

那么问题来了:由于一个割点可以属于多个点双 那么修改一个点的权值就要更新多个点双 如果这个图是一朵菊花 那么岂不直接被卡到O(n^2)?

因此 对于每个割点 我们只用这个割点所在虚点的父亲点双来维护这个点

如果所求的两个点的LCA是虚点 那么就统计这个虚点所代表的点的点权 如果LCA是点双 那么就把这个点双的父亲节点加入统计

然后基本就可以了 Tarjan求点双容易写挂 注意一下

看到神犇们的代码7KB+着实吓了一跳&…… 写完发现只有4KB…… 结果我第一,又快又好写……

#include <set>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
#define INF 0x3f3f3f3f
using namespace std;
struct BCC{
	multiset<int>s;
	int value;
	inline void Insert(int x)
	{
		s.insert(x);
		value=*s.begin();
	}
	inline void Modify(int x,int y)
	{
		s.erase( s.find(x) );
		s.insert(y);
		value=*s.begin();
	}
}BCCs[M<<1];
namespace ZKW_Segtree{
	int tree[263000<<1]={INF};
	int q;
	void Build_Tree(int a[],int n)
	{
		int i;
		for(q=1;q<=n+1;q<<=1);
		for(i=1;i<=n;i++)
			tree[q+i]=a[i];
		for(i=q-1;i;i--)
			tree[i]=min(tree[i<<1],tree[i<<1|1]);
	}
	void Modify(int x,int y)
	{
		tree[x+=q]=y;
		while(x>>=1)
			tree[x]=min(tree[x<<1],tree[x<<1|1]);
	}
	int Get_Ans(int x,int y)
	{
		int re=INF;
		for(x+=q-1,y+=q+1;x^y^1;x>>=1,y>>=1)
		{
			if(~x&1) re=min(re,tree[x^1]);
			if( y&1) re=min(re,tree[y^1]);
		}
		return re;
	}
}
int n,m,q,cnt;
int a[M],belong[M];
bool v[M];
namespace New_Map{
	struct abcd{
		int to,next;
	}table[M<<2];
	int head[M<<1],tot;
	int fa[M<<1],son[M<<1],dpt[M<<1],size[M<<1],top[M<<1],pos[M<<1],posf[M<<1],T;
	void Add(int x,int y)
	{
		table[++tot].to=y;
		table[tot].next=head[x];
		head[x]=tot;
	}
	void DFS1(int x)
	{
		int i;
		size[x]=1;
		dpt[x]=dpt[fa[x]]+1;
		for(i=head[x];i;i=table[i].next)
			if(table[i].to!=fa[x])
			{
				fa[table[i].to]=x;
				DFS1(table[i].to);
				size[x]+=size[table[i].to];
				if(size[table[i].to]>size[son[x]])
					son[x]=table[i].to;
			}
	}
	void DFS2(int x)
	{
		int i;
		if(son[fa[x]]==x)
			top[x]=top[fa[x]];
		else
			top[x]=x;
		posf[pos[x]=++T]=BCCs[x].value;
		if(son[x])
			DFS2(son[x]);
		for(i=head[x];i;i=table[i].next)
			if(table[i].to!=fa[x]&&table[i].to!=son[x])
				DFS2(table[i].to);
	}
	int LCA(int x,int y)
	{
		int fx=top[x],fy=top[y];
		while(fx!=fy)
		{
			if(dpt[fx]<dpt[fy])
				swap(x,y),swap(fx,fy);
			x=fa[fx];fx=top[x];
		}
		if(dpt[x]<dpt[y])
			swap(x,y);
		return y;
	}
	void Make_Graph()
	{
		int i;
		memset(posf,0x3f,sizeof posf);
		DFS1(n+1);DFS2(n+1);
		ZKW_Segtree::Build_Tree(posf,T);
	}
	int Query(int x,int y)
	{
		int re,fx=top[x],fy=top[y],temp=LCA(x,y);
		if(temp<=cnt) temp=fa[temp];re=a[temp-n];
		while(fx!=fy)
		{
			if(dpt[fx]<dpt[fy])
				swap(x,y),swap(fx,fy);
			re=min(re,ZKW_Segtree::Get_Ans(pos[fx],pos[x]));
			x=fa[fx];fx=top[x];
		}
		if(dpt[x]<dpt[y])
			swap(x,y);
		re=min(re,ZKW_Segtree::Get_Ans(pos[y],pos[x]));
		return re;
	}
}
namespace Ori_Map{
	struct abcd{
		int to,next;
	}table[M<<1];
	int head[M],tot;
	void Add(int x,int y)
	{
		table[++tot].to=y;
		table[tot].next=head[x];
		head[x]=tot;
	}
	void Tarjan(int x)
	{
		static int stack[M],top;
		static int dpt[M],low[M],T;
		int i;
		dpt[x]=low[x]=++T;
		stack[++top]=x;
		for(i=head[x];i;i=table[i].next)
		{
			if(dpt[table[i].to])
				low[x]=min(low[x],dpt[table[i].to]);
			else
			{
				Tarjan(table[i].to);
				low[x]=min(low[x],low[table[i].to]);
				if(dpt[x]==low[table[i].to])
				{
					int t;++cnt;
					v[x]=1;
					do{
						t=stack[top--];
						belong[t]=cnt;
						if(v[t])
						{
							New_Map::Add(t+n,cnt);
							New_Map::Add(cnt,t+n);
						}
					}while(t!=table[i].to);
					New_Map::Add(x+n,cnt);
					New_Map::Add(cnt,x+n);
				}
			}
		}
	}
	inline void Make_Graph()
	{
		int i;
		Tarjan(1);
		for(i=1;i<=n;i++)
		{
			if(i!=1)
				BCCs[ belong[i] ].Insert(a[i]);
			if(v[i]) BCCs[i+n].Insert(INF);
		}
		New_Map::Make_Graph();
	}
}
int main()
{
	
	//freopen("30.in","r",stdin);
	//freopen("30.out","w",stdout);
	
	int i,x,y;
	char p[10];
	cin>>n>>m>>q;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(i=1;i<=m;i++)
		scanf("%d%d",&x,&y),Ori_Map::Add(x,y),Ori_Map::Add(y,x);
	Ori_Map::Make_Graph();
	for(i=1;i<=q;i++)
	{
		scanf("%s%d%d",p,&x,&y);
		if(p[0]=='C')
		{
			if(x!=1)
			{
				BCCs[belong[x]].Modify(a[x],y);
				ZKW_Segtree::Modify(New_Map::pos[belong[x]],BCCs[belong[x]].value);
			}
			a[x]=y;
		}
		else
		{
			if(x==y)
			{
				printf("%d\n",a[x]);
				continue;
			}
			x=v[x]?x+n:belong[x];
			y=v[y]?y+n:belong[y];
			printf("%d\n", New_Map::Query(x,y) );
		}
	}
	return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值