JZOJ 7377.欢乐豆

\(\text{Problem}\)

有一个有向完全图,所有的 \(u\)\(v\) 的边权为 \(a_u\)
修改 \(m\) 此有向边边权,求最终图上两两点对的最短路之和
\(1\le n \le 10^5,1\le m \le 3000,1\le a_u \le 10^6\)

\(\text{Solution}\)

好题
如果没有修改边,那么 \(u\) 到任意一点的最短路为 \(a_u\)
有修改边,注意到 \(m\) 相对 \(n\) 很小,也就是说涉及的点很少,不妨称其为特殊点
显然特殊点个数最多为 \(O(m)\) 的,且特殊边也是 \(O(m)\)
考虑把特殊边连起来的点看成一个连通块(也就是视特殊边为无向边,把特殊点连起来)
我们导出这些特殊点构成的图,就是很多连通块的组成的图
这样再考虑两两点对间的最短路
无非分为特殊点到块内和到块外
\(1.\) 特殊点到块外就是特殊点到普通点和特殊点到不在此连通块内的特殊点
\(2.\) 特殊点到块内一定是特殊点到特殊点的最短路
先考虑 \(①\)
逐个考虑块内的点 \(x\) 到块外所有点的最短路
显然是此块内点 \(x\) 到此块内其它点 \(y\) 再到外面
\(dis(x,y)+a_y\) 的最小值
所以 \(①\) 贡献的最短路和为 \((n-cnt) \cdot min\)
再考虑 \(②\)
通过某种手段求出块内全源最短路,将这些 \(dis\) 加起来即可
有特殊边,有普通边,普通边是形如 \(a_u\) 的只和此端点有关
枚举起点,用线段树维护 \(dis\)
特殊边单点修改松弛,特殊边没被影响过的点用普通边松弛,这些点分散在多个区间,线段树上区间修改即可
然后就完成了,确实有点麻烦,但思路很清晰

\(\text{Code}\)

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#define ls (p << 1)
#define rs (ls | 1)
#define re register
using namespace std;
typedef long long LL; 

const int N = 1e5 + 5;
const LL INF = 1e15;
int n, m, a[N], id[N], Q[N], cnt, tot, Tot, bz[N], h[N], H[N], color;
LL dis[N];
vector<int> d[N];
struct edge{int to, nxt, w;}e[N];
inline void add_edge(int x, int y, int z){e[++tot] = edge{y, h[x], z}, h[x] = tot;}
struct Edge{int to, nxt;}E[N];
inline void add_Edge(int x, int y){E[++Tot] = Edge{y, H[x]}, H[x] = Tot;}
struct node{int x; LL s;}b[N];
inline node Min(node a, node b){return (a.s <= b.s ? a : b);}
inline bool cmp(node a, node b){return a.s < b.s;}

inline void read(int &x)
{
	x = 0; char ch = getchar(); int f = 1;
	for(; !isdigit(ch); f = (ch == '-' ? -1 : f), ch = getchar());
	for(; isdigit(ch); x = (x<<3) + (x<<1) + (ch^48), ch = getchar());
	x *= f;
}

void dfs(int x)
{
	bz[x] = color, Q[++cnt] = x;
	for(re int i = H[x]; i; i = E[i].nxt) if (!bz[E[i].to]) dfs(E[i].to);
}

LL tag[N >> 1], Mn[N >> 1]; int Mp[N >> 1];
inline void pushup(int p)
{
	if (Mn[ls] <= Mn[rs]) Mn[p] = Mn[ls], Mp[p] = Mp[ls];
	else Mn[p] = Mn[rs], Mp[p] = Mp[rs];
}
void build(int p, int l, int r)
{
	tag[p] = Mn[p] = INF;
	if (l == r) return void(Mp[p] = l);
	int mid = l + r >> 1;
	build(ls, l, mid), build(rs, mid + 1, r), pushup(p);
}
inline void pushdown(int p)
{
	if (tag[p] == INF) return;
	if (Mn[ls] != INF + 1) Mn[ls] = min(Mn[ls], tag[p]), tag[ls] = min(tag[ls], tag[p]);
	if (Mn[rs] != INF + 1) Mn[rs] = min(Mn[rs], tag[p]), tag[rs] = min(tag[rs], tag[p]);
	tag[p] = INF;
}
void modify(int p, int l, int r, int tl, int tr, LL v)
{
	if (tl > r || tr < l || v >= tag[p]) return;
	if (tl <= l && r <= tr) 
	{
		if (Mn[p] != INF + 1) Mn[p] = min(Mn[p], v), tag[p] = min(tag[p], v);
		return;
	}
	pushdown(p);
	int mid = l + r >> 1;
	if (tl <= mid) modify(ls, l, mid, tl, tr, v);
	if (tr > mid) modify(rs, mid + 1, r, tl, tr, v);
	pushup(p);
}
void pushflag(int p, int l, int r, int x)
{
	if (l == r) return void(Mn[p] = tag[p] = INF + 1);
	pushdown(p);
	int mid = l + r >> 1;
	if (x <= mid) pushflag(ls, l, mid, x);
	else pushflag(rs, mid + 1, r, x);
	pushup(p);
}

void solve()
{
	LL ans = 0, lst = 0;
	for(re int i = 1; i <= n; i++)
	if (!bz[i])
	{
		cnt = 0, ++color, dfs(i), sort(Q + 1, Q + cnt + 1);
		int r = 1;
		while (bz[b[r].x] == color && r <= n) ++r;
		for(re int l = 1; l <= cnt; l++) id[Q[l]] = l;
		for(re int l = 1; l <= cnt; l++)
		{
			build(1, 1, cnt), modify(1, 1, cnt, l, l, 0);
			for(re int j = 1; j <= cnt; j++)
			{
				node now = node{Mp[1], Mn[1]};
				for(re int k = h[Q[now.x]]; k; k = e[k].nxt) modify(1, 1, cnt, id[e[k].to], id[e[k].to], now.s + e[k].w);
				if (d[Q[now.x]].size())
				{
					modify(1, 1, cnt, 1, id[d[Q[now.x]][0]] - 1, now.s + a[Q[now.x]]);
					for(re int k = 1; k < d[Q[now.x]].size(); k++)
						modify(1, 1, cnt, id[d[Q[now.x]][k - 1]] + 1, id[d[Q[now.x]][k]] - 1, now.s + a[Q[now.x]]);
					modify(1, 1, cnt, id[d[Q[now.x]][d[Q[now.x]].size() - 1]] + 1, cnt, now.s + a[Q[now.x]]);
				}
				else modify(1, 1, cnt, 1, cnt, now.s + a[Q[now.x]]);
				if (r <= n) modify(1, 1, cnt, 1, cnt, now.s + a[Q[now.x]] + b[r].s);
				ans += (dis[now.x] = now.s), pushflag(1, 1, cnt, now.x);
			}
			LL mi = INF;
			for(re int j = 1; j <= cnt; j++) mi = min(mi, dis[j] + a[Q[j]]);
			ans += mi * (n - cnt);
		}
	}
	printf("%lld\n", ans);
}

int main()
{
	freopen("happybean.in", "r", stdin), freopen("happybean.out", "w", stdout);
	read(n), read(m);
	for(re int i = 1; i <= n; i++) read(a[i]), b[i].x = i, b[i].s = a[i]; sort(b + 1, b + n + 1, cmp);
	for(re int i = 1, x, y, z; i <= m; i++)
		read(x), read(y), read(z), add_edge(x, y, z), d[x].push_back(y), add_Edge(x, y), add_Edge(y, x);
	for(re int i = 1; i <= n; i++) sort(d[i].begin(), d[i].end());
	solve();
}
posted @ 2021-11-13 14:19  leiyuanze  阅读(57)  评论(0编辑  收藏  举报