在ZBQC随机游走

前言

这道题总思考时间不到 \(1\) 分钟,获得了 \(0pt\) 的好成绩!

原因竟是我在 T3 试图用 \(O(n)\) 的时间解决二维偏序问题!

题目

\(\rm 4s\ 256MB.\)

\(\tt PPL\) 在做不出来题的时候喜欢随机游走。

可以将 \(\rm ZBQC\) 看做有 \(n\) 个点的有向完全图,每个点有一个权值 \(a_u\),表示 \(u\rightarrow v\) 的边权。

但是 \(\rm ZBQC\) 并不是一成不变的,它有时候也会翻新,当然次数很少,具体的,它接下来有 \(m\) 翻新操作,每次操作会更改一条边的边权。

这天 \(\tt PPL\) 在随机游走的时候想到一个问题,如果他把所有点对 \((i,j)\) 之间的最短路都走一遍,总路程是多少?

显然 \(\tt PPL\) 不会算,只能交给你了。

\(1\le n\le 10^5;1\le m\le 3000;0\le a_i\le 10^6.\)

讲解

从数据范围入手,我们发现 \(m\) 很小,和 \(n\) 不是一个数量级,思考有什么用。

这里大概有个经典 \(\tt Trick\),在这种完全图中,存在一些特殊的边/点(比如这道题就是存在一些改了的边),一般来说这样构成的连通块会比较小,把这些特殊的边/点构成的连通块处理一下,再处理一下块与块之间的信息,题就解决了。

这玩意好像之前碰到过一次,不然我为什么觉得是经典 Trick。

回到这道题,考虑把每个弱连通块内部的最短路处理出来,显然连通块总大小不会超过 \(m\)。这时我们可以枚举起点,再用线段树优化 \(\tt dijkstra\) 来得到块内的最短路,注意块内的最短路不一定只在块内走,有可能出块再入块,这种情况下一定是出去到一个最小的 \(a\) 再回来,然后我们就得到了块内的贡献。

出块的贡献就很简单了,只需要在块内找一个最优的点出去即可。

时间复杂度 \(O(n+m^2\log_2m)\)

据说有个坑点:一条边可能会被修改多次,要取最后那次。不知道数据有没有这样的情况,反正我判了一下。

代码

不卡常!
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;

typedef long long LL;
const int MAXN = 100005;
const int INF = 0x3f3f3f3f;
int n,m;
int a[MAXN],suf[MAXN];
LL ans;

LL Read()
{
	LL x = 0,f = 1; char c = getchar();
	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

struct edge
{
	int v,w;
	bool operator < (const edge &px)const{
		return v < px.v;
	}
};
set<edge> G[MAXN]; 

int f[MAXN];
int findSet(int x){if(f[x]^x)f[x]=findSet(f[x]);return f[x];}

vector<int> p[MAXN];

#define lc (x<<1)
#define rc (x<<1|1)
int MIN[MAXN<<2],ID[MAXN<<2],lz[MAXN<<2];
void calc(int x,int val){if(MIN[x]^(INF+1))MIN[x] = Min(MIN[x],val),lz[x] = Min(lz[x],val);}
void up(int x)
{
	if(MIN[lc] <= MIN[rc]) MIN[x] = MIN[lc],ID[x] = ID[lc];
	else MIN[x] = MIN[rc],ID[x] = ID[rc];
}
void down(int x)
{
	if(lz[x] == INF) return;
	calc(lc,lz[x]); calc(rc,lz[x]);
	lz[x] = INF;
}
void Build(int x,int l,int r,int now)
{
	lz[x] = INF;
	if(l == r) {if(l^now)MIN[x] = INF;else MIN[x] = 0;ID[x] = l;return;}
	int mid = (l+r) >> 1;
	Build(lc,l,mid,now); Build(rc,mid+1,r,now);
	up(x);
}
void Change(int x,int l,int r,int ql,int qr,int val)
{
	if(ql > qr) return;
	if(ql <= l && r <= qr) {calc(x,val);return;}
	int mid = (l+r) >> 1;
	down(x);
	if(ql <= mid) Change(lc,l,mid,ql,qr,val);
	if(mid+1 <= qr) Change(rc,mid+1,r,ql,qr,val);
	up(x);
}
void fk(int x,int l,int r,int pos)
{
	if(l == r){MIN[x] = INF+1;return;}//INF+1 : unchangeable
	int mid = (l+r) >> 1;
	down(x);
	if(pos <= mid) fk(lc,l,mid,pos);
	else fk(rc,mid+1,r,pos);
	up(x);
}
int dis[MAXN],to[MAXN];

int main()
{
//	freopen("happybean.in","r",stdin);
//	freopen("happybean.out","w",stdout);
	n = Read(); m = Read();
	for(int i = 1;i <= n;++ i) a[i] = Read(),f[i] = i;
	for(int i = 1;i <= m;++ i)
	{
		int u = Read(),v = Read(),w = Read();
		int U = findSet(u),V = findSet(v);
		if(U > V) f[U] = V;
		else f[V] = U;
		auto it = G[u].lower_bound(edge{v,0});
		if(it->v == v) G[u].erase(it);
		G[u].insert(edge{v,w});
	}
	for(int i = 1;i <= n;++ i) p[findSet(i)].emplace_back(i);
	suf[n+1] = INF;
	for(int i = n;i >= 1;-- i)
	{
		suf[i] = suf[i+1];
		for(auto A : p[i]) suf[i] = Min(suf[i],a[A]);
	}
	for(int i = 1,nowmin = INF;i <= n;++ i)
	{
		if(!p[i].size()) continue;
		for(int j = 0,N = p[i].size()-1;j <= N;++ j) to[p[i][j]] = j;
		for(int j = 0,N = p[i].size()-1;j <= N;++ j)//begining
		{
			Build(1,0,N,j);
			for(int k = 0;k <= N;++ k)//dijkstra : pop queue
			{
				int now = ID[1],lst = 0,x = p[i][now]; dis[now] = MIN[1];
				for(auto A : G[p[i][now]])
				{
					Change(1,0,N,lst,to[A.v]-1,dis[now]+a[x]);
					Change(1,0,N,to[A.v],to[A.v],dis[now]+A.w);
					lst = to[A.v]+1;
				}
				if(lst <= N) Change(1,0,N,lst,N,dis[now]+a[x]);
				fk(1,0,N,now);
			}
			int wdnmd = a[p[i][j]]+Min(nowmin,suf[i+1]);
			for(int k = 0;k <= N;++ k) dis[k] = Min(dis[k],wdnmd);
			int v = INF;
			for(int k = 0;k <= N;++ k) ans += dis[k],v = Min(v,dis[k]+a[p[i][k]]);//inside
			ans += v * (n-N-1ll);//outside
		}
		for(auto A : p[i]) nowmin = Min(nowmin,a[A]);
	}
	Put(ans,'\n');
	return 0;
}
posted @ 2021-11-10 21:12  皮皮刘  阅读(58)  评论(0编辑  收藏  举报