图论两题:51Nod1743雪之国度&&CF Round 51F(div 2)

雪之国度有N座城市,依次编号为1到N,又有M条道路连接了其中的城市,每一条道路都连接了不同的2个城市,任何两座不同的城市之间可能不止一条道路。
雪之女王赋予了每一座城市不同的能量,其中第i座城市被赋予的能量为Wi。
如果城市u和v之间有一条道路,那么只要此刻雪之女王的能量不小于|Wu-Wv|,这条道路就是安全的。
如果城市u和v之间存在两条没有重复道路的安全路径(其中每一段道路都是安全的),则认为这两座城市之间有着良好的贸易关系。
最近,雪之女王因为情感问题,她的能量产生巨大的波动。为了维持雪之国度的经济贸易,她希望你能帮忙对Q对城市进行调查。
对于第j对城市uj和vj,她希望知道在保证这两座城市之间有着良好贸易关系的前提之下,自己最少需要保持多少的能量。
输入
每一组数据第一行有3个整数,依次为N,M,Q,表示城市个数,道路个数,和所需要进行的调查次数。
之后一行,有N个整数,依次为每一个城市被赋予的能量Wi。
之后M行,每一行有2个整数,表示对应编号的两个城市之间有一条道路。
之后Q行,每一行有2个整数,表示一组调查的城市目标。
对于100%的数据来说,3<=N<=100000, 3<=M<=500000, 1<=Q<=100000, 每一座城市的能量Wi满足0<=Wi<=200000.
输出
输出一共有Q行,依次对应Q次调查的结果。
其中第j行给出了第j次调查的结果,即雪之女王需要保持的最少能量值。如果永远也无法做到,输出"infinitely"。
输入样例
7 8 4
3 2 4 1 3 5 9
1 2
1 3
2 4
2 5
3 6
6 7
4 6
5 6
4 5
4 6
5 6
2 7
输出样例
4
4
2
infinitely


可以把两个点权值之差看作这两点之间边的权值,先做一遍最小生成树,那么最小生成树上的点肯定不是答案的点(因为答案要求一个双联通分量里面最大的边的最小值,而最小生成树只有单连通)。
再考虑每次加入一条新的非树边(按权值从小到大),那么这个新边肯定会构成一个环,也就是一个双联通分量。于是对于这个环上的所有未被访问的边来说,它们所需要的最小的能量值就是新加进边的权值。并且它们的状态接下去就标记为访问过。
这样的正确性显然是可以证明的,因为新加进的边形成的环中,如果某些边已经被访问过,那么说明它们之前就已经在一个环中,那个环的最大边的权值肯定小于等于目前的环,所以对于那些边来说,那个环肯定更优,所以就不更新。而未访问过的边显然与新加入的这条边能构成最优情况,因为即是后面再加入边也能和这条边形成环,那么环上最大权值也大于等于目前环。所以目前肯定最优。
可以用并查集来维护是否被访问过,如果被访问过之后,就记录fa[x]=F[x][0]也就是将并查集里的 f a fa fa直接标记为它的父亲,那么下次访问这个点的时候直接往父亲上跳,如果父亲也访问过,还会继续上跳。知道跳到未访问的节点。所以我们对于新加入的非树边,都从其两端点这样跳,最后理论复杂度就是 O ( n ) O(n) O(n),因为每个点只会访问一次。
最后统计答案,就做一遍LCA,在倍增的时候顺便取访问过的边能量值,取 m a x max max即可。如果情况为 i n f i n i t e l y infinitely infinitely,那也就是说中间有点没访问过,那么在并查集里的表现就是find(x)!=find(y)
#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;
int read(){
	char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
	while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,m,q,cnt,dfn,w[MAXN],fa[MAXN],vis[MAXN*5];
int head[MAXN],nxt[MAXN*2],go[MAXN*2],dep[MAXN];
int Max[MAXN][20],F[MAXN][20];
struct node{
	int a,b,len;
	bool operator<(const node &x)const{
		return len<x.len;
	}
}f[MAXN*5];
int find(int x){
	if(fa[x]!=x) fa[x]=find(fa[x]);
	return fa[x];
}
void add(int x,int y){
	go[cnt]=y;nxt[cnt]=head[x];head[x]=cnt;cnt++;
	go[cnt]=x;nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
void dfs(int x,int f){
	F[x][0]=f;dep[x]=dep[f]+1;
	for(int i=head[x];i!=-1;i=nxt[i]){
		int to=go[i];
		if(to==f) continue;
		dfs(to,x);
	}
}
void pre(){
	  for(int i=1;(1<<i)<=n;i++)
	for(int x=1;x<=n;x++){
		  F[x][i]=F[F[x][i-1]][i-1];
		  Max[x][i]=max(Max[x][i-1],Max[F[x][i-1]][i-1]);
	  }
}
int lca(int x,int y){
	int ans=0;
	if(dep[x]>dep[y]) swap(x,y);
	for(int i=17;i>=0;i--)
	  if(dep[F[y][i]]>=dep[x]) ans=max(ans,Max[y][i]),y=F[y][i];
	for(int i=17;i>=0;i--)
	  if(F[x][i]!=F[y][i]){
	  	ans=max(ans,max(Max[x][i],Max[y][i]));x=F[x][i],y=F[y][i];
	  }
	if(x!=y) ans=max(ans,max(Max[x][0],Max[y][0]));
	return ans;
}
int main()
{
	n=read();m=read();q=read();
	memset(head,-1,sizeof(head));
	for(int i=1;i<=n;i++) w[i]=read();
	for(int i=1;i<=m;i++){
		f[i].a=read();f[i].b=read();f[i].len=abs(w[f[i].a]-w[f[i].b]);
	}
	for(int i=1;i<=n;i++) fa[i]=i;
	sort(f+1,f+1+m);int k=0;
	for(int i=1;i<=m;i++){
		if(k==n-1) break;
		int x=find(f[i].a),y=find(f[i].b);
		if(x!=y){
			fa[y]=x;k++;add(f[i].a,f[i].b);vis[i]=1;
		}
	}
	dfs(1,0);
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++){
		if(vis[i]) continue;
		int x=find(f[i].a),y=find(f[i].b);
		while(x!=y){
			if(dep[x]<dep[y]) swap(x,y);
			Max[x][0]=f[i].len;fa[x]=F[x][0];x=find(x);
		}
	}
	pre();
	for(int i=1;i<=q;i++){
		int x=read(),y=read();
		if(find(x)!=find(y)) puts("infinitely");
		else printf("%d\n",lca(x,y));
	}
	return 0;
}
题目传送门
大意:一张图,N个点,M条无向边,保证 M − N ≤ 20 M-N\leq 20 MN20。总共Q组询问,每次询问某两点之间的最短路。
其中 N , M , Q ≤ 1 0 5 N,M,Q\leq 10^5 N,M,Q105

因为 M − N ≤ 20 M-N\leq 20 MN20,所以我们可以随便建一棵树(我建的是最小生成树,当然也可以乱建)。那么最多只剩下21条非树边了。
对于这21条边,我们将它的两个端点都做一次SPFA,求出与其有关的最短路,那么最后统计答案时就是:
		ll x=read(),y=read();
		ans=sum[x]+sum[y]-sum[lca(x,y)]*2;  //两点在树上的最短路
		for(j=1;j<=tot;j++){
			ans=min(ans,dis[j][x]+dis[j][y]);  //经过每个端点的最短路
		}
这道题题解虽短,但是却是道好题,要细细观察,抓住性质,脑洞大开,才能干掉。
#include<bits/stdc++.h>
#define MAXN 100005
#define mp make_pair
#define pa pair<ll,ll>
#define ll long long
using namespace std;
ll read(){
	char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
	while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
void print(ll x){
	if(x/10) print(x/10);
	putchar(x%10+'0');
}
ll n,m,k,tot,fa[MAXN],vis[MAXN],f[MAXN][21],dep[MAXN],ask[MAXN],sum[MAXN];
ll cnt,qr,ans,head[MAXN<<1],nxt[MAXN<<1],go[MAXN<<1],dis[43][MAXN];
map<ll,ll> p;
queue<ll> q;
vector<pa> r[MAXN];
struct node{
	ll a,b,len;
	bool operator<(const node x)const{
		return len<x.len;
	}
}F[MAXN];
struct edge{
	ll to,val;
}L[MAXN<<1];
ll find(ll x){
	if(fa[x]!=x) fa[x]=find(fa[x]);
	return fa[x];
}
void add(ll x,ll y,ll c){
	L[cnt]=(edge){y,c};
	nxt[cnt]=head[x];head[x]=cnt;cnt++;
	L[cnt]=(edge){x,c};
	nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
void dfs(ll x,ll fa){
	register int i;
	f[x][0]=fa;dep[x]=dep[fa]+1;
	for(i=1;(1<<i)<=dep[x];i++) f[x][i]=f[f[x][i-1]][i-1];
	for(i=head[x];i!=-1;i=nxt[i]){
		ll to=L[i].to;
		if(to==fa) continue;
		sum[to]=sum[x]+L[i].val;dfs(to,x);
	}
}
void spfa(ll x,ll p){
	register int i;
	memset(dis[p],63,sizeof(dis[p]));
	dis[p][x]=0;q.push(x);ask[x]=1;
	while(!q.empty()){
		ll now=q.front();q.pop();ask[now]=0;
		for(i=0;i<r[now].size();i++){
			ll to=r[now][i].first;
			if(dis[p][now]+r[now][i].second<dis[p][to]){
				dis[p][to]=dis[p][now]+r[now][i].second;
				if(!ask[to]){
					ask[to]=1;q.push(to);
				}
			}
		}
	}
}
ll lca(ll x,ll y){
	register int i;
	if(dep[x]>dep[y]) swap(x,y);
	for(i=17;i>=0;i--)
	  if(dep[f[y][i]]>=dep[x]) y=f[y][i];
	for(i=17;i>=0;i--)
	  if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	if(x!=y) x=f[x][0];
	return x;
}
int main()
{
	n=read();m=read();
	register int i,j;
	memset(head,-1,sizeof(head));
	for(i=1;i<=n;i++) fa[i]=i;
	for(i=1;i<=m;i++){
		F[i].a=read();F[i].b=read();
		F[i].len=read();
		r[F[i].a].push_back(mp(F[i].b,F[i].len));r[F[i].b].push_back(mp(F[i].a,F[i].len));
	}
	sort(F+1,F+1+m);k=0;
	for(i=1;i<=m;i++){
		if(k==n-1) break;
		ll x=find(F[i].a),y=find(F[i].b);
		if(x!=y){
			fa[y]=x;k++;vis[i]=1;
			add(F[i].a,F[i].b,F[i].len);
		}
	}
	dfs(1,0);
	for(i=1;i<=m;i++){
		if(vis[i]) continue;
		if(!p[F[i].a]) p[F[i].a]=++tot,spfa(F[i].a,tot);
		if(!p[F[i].b]) p[F[i].b]=++tot,spfa(F[i].b,tot);
	}
	qr=read();
	for(i=1;i<=qr;i++){
		ll x=read(),y=read();ans=sum[x]+sum[y]-sum[lca(x,y)]*2;
		for(j=1;j<=tot;j++){
			ans=min(ans,dis[j][x]+dis[j][y]);
		}
		print(ans);puts("");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值