【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