ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

欢乐豆(happybean)

2021-10-16 11:03:27  阅读:177  来源: 互联网

标签:happybean ch int 短路 tr leq 欢乐 rightarrow


\(happybean\)

题目大意

给定一个有向完全图,其中 \(u\rightarrow v\) 的边权为 \(a_u\) 。

进行 \(m\) 次修改,第 \(i\) 次修改给定 \(x,y,z\) ,将 \(x\rightarrow y\) 的有向边边权改为 \(z\) 。

求所有点对 \((i,j)\) 且 \(i\neq j\) 的最短路之和。

对于所有数据,满足 \(1\leq n\leq 10^5,1\leq m\leq 3000,0\leq a_i\leq 10^5\) 。

分析

考虑暴力做法,\(Floyd\) 能够很轻松的过掉 \(O(n^3)\) ,但显然是过不去这题的。

转化

我们发现,在整张图中,除了边权背修改后的点,事实上我们还有很多没有改动的点,于是我们也许能够考虑将边权被改变的子图导出来考虑。

若将其忽视,显然我们能够得到若干个连通块。

怎么计算 \((x,y)\) 的最短路:

  • 若 \(x\) 与 \(y\) 不属于同一个连通块,则 \(x\rightarrow\) 的路径如下:

    • 若 \(dis_i\) 表示 \(x\rightarrow i\) 的最短路,那么 \(x\rightarrow y\) 的最短路显然是 \(min(dis_i+a_i)\) 其中 \(i\) 是与 \(x\) 连通块相同的点,这不难理解。

于是,我们好像把问题转化为了求单一连通块内的最短路。

对于该最短路,有一个细节,\(x\rightarrow y\) 的路径,可以是 \(x\rightarrow z\) 再从 \(z\rightarrow y\) ,其中 \(z\) 是连通块外一点,可以发现 \(z\) 一定满足 \(a_z\) 最小。

优化

由于 \(m\leq 3000\) ,且我们要求的是全源最短路,且边数巨大,普通的最短路算法显然无法解决,考虑用一颗线段树来模拟 \(DJ\) 的过程,更新时,只需要单点更新当前点有边相连的点,对于其他点可以直接区间修改(注:没有边相连的点实际上是他们之间的连边没有被修改) 。

这样我们就能做到 \(O(m^2log m)\) ,过掉这道题。

统计答案时注意统计连通块内自己的答案。

加上转化中的说的那个细节,总的时间复杂度变成了 \(O(n+m^2logm)\) 。

CODE

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=3e3+10,INF=1e9;
inline int read()
{
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w*=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
	return s*w;
}
struct tree{ int mi,pos,tag,sum; }tr[4*M];
struct edge{ int to,nex,w; }e[M];
struct node{
	int x,y;
	bool operator <(const node A)const { return x<A.x; }
};
int n,m;
long long ans;
int X,Y,Z;
int a[N],arr[N],smn[N],fa[N],id[N];
int dis[M];
int tot,first[N];
vector<int> v[N]; //v[i]储存连同块i内的节点
vector<node> E[M];
inline int find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
inline void Add(int x,int y,int z)
{
	e[++tot].nex=first[x];
	first[x]=tot;
	e[tot].to=y,e[tot].w=z;
}
inline void update(int k)
{
	if(!tr[k].sum) { tr[k].mi=INF; return; }
	if(tr[k*2].mi<tr[k*2+1].mi) tr[k].mi=tr[k*2].mi,tr[k].pos=tr[k*2].pos;
	else tr[k].mi=tr[k*2+1].mi,tr[k].pos=tr[k*2+1].pos;
}
inline void Benew(int k,int v)
{
	if(!tr[k].sum) return;
	tr[k].tag=min(tr[k].tag,v);
	tr[k].mi=min(tr[k].mi,v);
}
inline void pushdown(int k)
{
	if(tr[k].tag^INF){
		Benew(k*2,tr[k].tag),Benew(k*2+1,tr[k].tag);
		tr[k].tag=INF;
	}
}
inline void delet(int k,int l,int r) //删除操作 
{
	--tr[k].sum;
	if(l==r) { tr[k].pos=0,tr[k].mi=INF; return; }
	pushdown(k);
	int mid=(l+r)/2;
	if(X<=mid) delet(k*2,l,mid);
	else delet(k*2+1,mid+1,r);
	update(k);
}
inline void change(int k,int l,int r)
{
	if(!tr[k].sum) return;
	if(l>=X&&r<=Y) return Benew(k,Z);
	pushdown(k);
	int mid=(l+r)/2;
	if(X<=mid) change(k*2,l,mid);
	if(Y>mid) change(k*2+1,mid+1,r);
	update(k);
}
inline void build(int k,int l,int r)
{
	tr[k].sum=r-l+1,tr[k].tag=INF;
	if(l==r) { tr[k].mi=INF-1,tr[k].pos=l; return; }
	int mid=(l+r)/2;
	build(k*2,l,mid),build(k*2+1,mid+1,r);
	update(k);
}
inline void Solve(int x)
{
	for(register int i=0;i<v[x].size();i++) id[v[x][i]]=i+1;
	int lim=v[x].size();
	for(register int i=1;i<=lim;i++) E[i].clear();
	for(register int i=0;i<lim;i++)
		for(register int j=first[v[x][i]];j;j=e[j].nex)
			E[i+1].push_back((node){id[e[j].to],e[j].w}); //新连边 
	for(register int i=1;i<=lim;i++) sort(E[i].begin(),E[i].end()); //排序方便区间修改 
	for(register int i=1;i<=lim;i++){
		build(1,1,lim);
		X=Y=i,Z=0;
		change(1,1,lim);
		//线段树维护一个 DJ 过程-
		for(register int j=1;j<=lim;j++){
			int z=tr[1].pos; //找到当前 dis 最小的节点
			dis[z]=tr[1].mi; //给该点赋值 
	//		printf("%d %d\n",z,dis[z]);
			X=z,delet(1,1,lim); //注意删除该点 
			int temp=0; //temp维护上次修改的末端 
			for(register int k=0;k<E[z].size();k++){
				if(E[z][k].x-1>temp) X=temp+1,Y=E[z][k].x-1,Z=dis[z]+a[v[x][z-1]],change(1,1,lim); //区间修改 
				X=Y=E[z][k].x,Z=dis[z]+min(a[v[x][z-1]]+min(arr[x-1],smn[x+1]),E[z][k].y),change(1,1,lim),temp=E[z][k].x; //单点修改 
			}
			if(temp^lim) X=temp+1,Y=lim,Z=dis[z]+a[v[x][z-1]],change(1,1,lim);
		}
		int mi=a[v[x][i-1]];
		for(register int j=1;j<=lim;j++) if(j^i) ans+=dis[j],mi=min(mi,dis[j]+a[v[x][j-1]]);
		ans+=1ll*mi*(n-v[x].size()); 
	}
}
int main()
{
	n=read(),m=read();
	for(register int i=1;i<=n;i++) a[i]=read(),fa[i]=i;
	for(register int i=1;i<=m;i++){
		int x=read(),y=read(),z=read();
		Add(x,y,z);
		fa[find(x)]=find(y); //并查集找连通块 
	}
	for(register int i=1;i<=n;i++) v[find(i)].push_back(i); 
	arr[0]=INF;
	for(register int i=1;i<=n;i++){
		arr[i]=arr[i-1];
		if(i==find(i))
			for(register int j=0;j<v[i].size();j++) arr[i]=min(arr[i],a[v[i][j]]);
	}
	smn[n+1]=INF;
	for(register int i=n;i;i--){
		smn[i]=smn[i+1];
		if(i==find(i))
			for(register int j=0;j<v[i].size();j++) smn[i]=min(smn[i],a[v[i][j]]);
	}
	for(register int i=1;i<=n;i++) if(i==find(i)) Solve(i);
	printf("%lld\n",ans);
	return 0;
}

标签:happybean,ch,int,短路,tr,leq,欢乐,rightarrow
来源: https://www.cnblogs.com/Defoliation-ldlh/p/15413585.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有