【2021.11.11NOIP提高组联考】欢乐豆(happybean)题解

【2021.11.11NOIP提高组联考】欢乐豆(happybean) 题解

Description

自己看

Solution

先思考m=0的部分分
发现每个点到其他所有点的最短路就是 a x a_x ax
这启发我们,只用考虑把被修改的边视作无向边,所形成的连通块的内部最短路
先来考虑处理出块内最短路后怎样得到答案
我们枚举源点,显然对于任意块外的 z z z, ( x , z ) (x, z) (x,z)都可视作 ( x , y ) ∪ ( y , z ) (x , y) \cup (y, z) (x,y)(y,z)(y在块内).
那么找到块内 d i s t ( x , y ) dist(x,y) dist(x,y)最小的 y y y即可.
考虑计算块内最短路
首先一种情况, x x x经块外的 z z z y y y,显然最优的 z z z满足 a z a_z az最小.
第二种情况,我们只能跑最短路算法了.
这里如果用 O ( m 3 ) O(m^3) O(m3) F l o y d Floyd Floyd算法实现,只能拿到 40 p t s 40pts 40pts.
注意到被修改了的边是很少的.我们考虑基于这点性质优化最短路算法.
F l o y d Floyd Floyd难以优化,考虑对于每个源点跑 D i j k s t r a Dijkstra Dijkstra算法.
因为需要维护的点很多,我们用线段树维护,那么只需要支持两个操作
1.维护全局最小值.
2.对于当前的最小值,对其他所有节点做松弛操作.
这个可以用一个维护 m i n min min的线段树实现.
怎么松弛呢?
用最暴力的办法.
假设源点是 x x x,枚举每个点连出去的被修改过的边,那么对应的点就会有 y 1 , y 2 . . . . . y_1,y_2..... y1,y2.....
对于每个 y i y_i yi单点取min,对于每个 ( y i , y i + 1 ) (y_i,y_{i+1}) (yi,yi+1)区间取min.
乍一看复杂度非常垃圾,但是每个源点跑完一次 d i j dij dij只会有 O ( m ) O(m) O(m)个对应的 y y y,所以单点修改的区间修改次数都是 O ( m ) O(m) O(m)的.所以一次 d i j dij dij的复杂度就是 O ( m log ⁡ m ) O(m\log{m}) O(mlogm),所以处理块内最短路就是 ( O ( m 2 log ⁡ m ) ) (O(m^2\log{m})) (O(m2logm))
那么总复杂度就是 O ( n + m 2 log ⁡ m ) O(n + m^2\log{m}) O(n+m2logm)的了.

Code

#pragma GCC optimize(3)
#pragma GCC optimize("inline")
#pragma GCC optimize("Ofast")
#pragma GCC target("sse3","sse2","sse")
#pragma GCC diagnostic error "-std=c++14"
#pragma GCC diagnostic error "-fwhole-program"
#pragma GCC diagnostic error "-fcse-skip-blocks"
#pragma GCC diagnostic error "-funsafe-loop-optimizations"
#pragma GCC optimize("fast-math","unroll-loops","no-stack-protector","inline")
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#define LL long long
#define RE register
#define pii pair <int, int>
#define mp make_pair
#define fr first
#define sc second
#define IN inline
using namespace std;
IN int read() {
	int res = 0; char ch = getchar();
	for(; !isdigit(ch); ch = getchar());
	for(; isdigit(ch); ch = getchar()) res = (res << 1) + (res << 3) + (ch ^ 48);
	return res;
}
const int N = 1e5 + 10, M = 3010;
const LL inf = 1e9;
int fa[N], m, a[N], tag[M * 4], flag[M * 4], p[M], q[N], b[N];
LL ans, dis[M], n;
pii tr[M * 4];
int getfa(int x) {return fa[x] == x ? x : (fa[x] = getfa(fa[x]));}
vector <pii> edge[N];
vector <int> s[N];
pii minp(pii x, pii y) {return x < y ? x : y;}
pii operator + (pii x, pii y) {return mp(x.fr + y.fr, x.sc + y.sc);}
IN void build(int k, int l, int r) {
	tag[k] = inf, flag[k] = 0;
	if(l == r) {
		tr[k] = mp(inf, l);
		return ;
	}
	int mid = l + r >> 1;
	build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r);
	tr[k] = minp(tr[k << 1], tr[k << 1 | 1]);
	return ;
}
IN void pushdown(int k) {
	if(tag[k] == inf) return ;
	int ls = k << 1, rs = k << 1 | 1;
	if(!flag[ls]) tr[ls] = minp(tr[ls], mp(tag[k], tr[ls].sc));
	if(!flag[rs]) tr[rs] = minp(tr[rs], mp(tag[k], tr[rs].sc));
	tag[ls] = min(tag[ls], tag[k]), tag[rs] = min(tag[rs], tag[k]);
	tag[k] = inf;
	return ;
}
IN pii query(int k, int l, int r, int locl, int locr) {
	if(l > r) return mp(inf, inf);
	if(l > locr || r < locl) return mp(inf, inf);
	if(flag[k]) return mp(-1, -1);
	if(locl <= l && r <= locr) return tr[k];
	pushdown(k);
	int mid = l + r >> 1;
	return minp(query(k << 1, l, mid, locl, locr), query(k << 1 | 1, mid + 1, r, locl, locr));
}
IN void modify(int k, int l, int r, int locl, int locr, int val) {
	if(l > r) return ;
	if(l > locr || r < locl) return ;
	if(flag[k]) return ;
	if(locl <= l && r <= locr) {
		tr[k] = minp(tr[k], mp(val, tr[k].sc));
		tag[k] = min(tag[k], val);
		return ;
	}
//	if(tag[k] < val) return ;
	pushdown(k);
	int mid = l + r >> 1;
	modify(k << 1, l, mid, locl, locr, val), modify(k << 1 | 1, mid + 1, r, locl, locr, val);
	tr[k] = minp(tr[k << 1], tr[k << 1 | 1]);
	return ;
}
IN void modifyflag(int k, int l, int r, int loc) {
	if(l > loc || r < loc) return ;
	if(l == r) { flag[k] = 1; return ;}
	int mid = l + r >> 1;
	modifyflag(k << 1, l, mid, loc), modifyflag(k << 1 | 1, mid + 1, r, loc);
	flag[k] = flag[k << 1] & flag[k << 1 | 1];
	return ;
}
IN void modifycaonima(int k, int l, int r, int loc, int val) {
	if(l > loc || r < loc) return ;
	if(l == r) {
		tag[k] = 0;
		tr[k] = mp(val, tr[k].sc);
		return ;
	}
	pushdown(k);
	int mid = l + r >> 1;
	modifycaonima(k << 1, l, mid, loc, val),
	modifycaonima(k << 1 | 1, mid + 1, r, loc, val);
	tr[k] = minp(tr[k << 1], tr[k << 1 | 1]);
	return ;
}
inline void dij(int s, int len) {
	build(1, 1, len);
//	printf("KAISHI %d\n",query(1, 1, n, 1, 1).fr);
	modify(1, 1, len, s, s, 0);
	while(true) {
		pii now = query(1, 1, len, 1, len);
		if(now.fr == -1) return ;
		dis[now.sc] = now.fr;
//		printf("HELLO %d %d %d %d\n",p[now.sc],now.fr,edge[p[now.sc]][0].fr,edge[p[now.sc]][0].sc + dis[now.sc]);
		LL d = dis[now.sc] + a[p[now.sc]];
		if(edge[p[now.sc]].size()) modify(1, 1, len, 1, q[edge[p[now.sc]][0].fr] - 1, d);
		else modify(1, 1, len, 1, len, d);
		for(int i = 0; i < edge[p[now.sc]].size(); ++i) {
			modify(1, 1, len, q[edge[p[now.sc]][i].fr], q[edge[p[now.sc]][i].fr], edge[p[now.sc]][i].sc + dis[now.sc]);
//			modify(1, 1, len, 1, 1,edge[p[now.sc]][i].sc + dis[now.sc]);
//			printf("Work %d %d %d %d %d\n",p[now.sc],q[edge[p[now.sc]][i].fr],edge[p[now.sc]][i].fr,edge[p[now.sc]][i].sc + dis[now.sc],query(1, 1, n, 1, 1).fr);
			if(i < edge[p[now.sc]].size() - 1) modify(1, 1, len, q[edge[p[now.sc]][i].fr] + 1, q[edge[p[now.sc]][i + 1].fr] - 1, d);
			else modify(1, 1, len, q[edge[p[now.sc]][i].fr] + 1, len, d);
		}
		modifycaonima(1, 1, len, now.sc, inf);
		modifyflag(1, 1, len, now.sc);
	}
}
void work(int x) {
//	printf("%d %d\n",x,s[x].size());
	if(s[x].size() == 1) {
		ans += (n - 1) * a[x];
		return ;
	}
	LL mnn = inf;
	for(RE int i = 1; i <= n; ++i) {
		if(getfa(b[i]) == x) continue;
		mnn = a[b[i]];
		break; 
	}
//	printf("%lld\n",mnn);
	LL len = s[x].size();
	sort(s[x].begin(), s[x].end());
	for(RE int i = 1; i <= len; ++i)
		p[i] = s[x][i - 1], q[p[i]] = i;
	LL anss = ans;
	for(RE int i = 1; i <= len; ++i) {
		LL mn = inf;
		dij(i, len);
//		printf("HELLO\n");
//		for(int j = 1; j <= len; ++j)
//			printf("OLD %d %d %d\n",p[i],p[j],dis[j]);
		for(RE int j = 1; j <= len; ++j)
			dis[j] = min(dis[j], a[p[i]] + mnn);
		for(RE int j = 1; j <= len; ++j) {
//			printf("%d %d %d\n",p[i],p[j],dis[j]);
			if(j != i) ans += dis[j];
			if(dis[j] + a[p[j]] < mn) mn = dis[j] + a[p[j]];
		}
//		printf("%lld\n",mn);
		ans += (n - len) * mn;
	}
//		printf("%lld\n",ans - anss);
}
bool cmp (int x, int y) {return a[x] < a[y];}
bool cmp1(pii x, pii y) {return x.fr < y.fr;}
signed main() {
	freopen("sample_happybean4.in","r",stdin);
//	freopen("happybean.in","r",stdin);
//	freopen("happybean.out","w",stdout);
	n = read(), m = read();
	for(RE int i = 1; i <= n; ++i)
		a[i] = read(), fa[i] = b[i] = i;
	sort(b + 1, b + n + 1, cmp);
	for(RE int i = 1; i <= m; ++i) {
		int x = read(), y = read(), z = read();
		edge[x].push_back(mp(y, z));
		fa[getfa(y)] = getfa(x);
	} 
//	for(int i = 1; i <= n; ++i)
//		printf("%d %d\n",i,getfa(i));
	for(RE int i = 1; i <= n; ++i) 
		s[getfa(i)].push_back(i), sort(edge[i].begin(), edge[i].end());
	for(RE int i = 1; i <= n; ++i) 
		if(getfa(i) == i) work(i);
	printf("%lld\n",ans);
	return 0;
}

比我的多项式板子还长 Q W Q QWQ QWQ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值