[反悔贪心] [小清新] P7219 [JOISC2020] 星座 3
牛波一题。算是对其它题解不清楚地方的补充吧。
如果让构成星座的两对点(下文称为非法点对)互相连边,那么实际上就是在求删去后不存在点相邻的最小花费方案(是最大权独立集的对称问题),考虑贪心。
然后依次考虑节点。记录
然后如果删了一个点那么因为这个点而删掉的就不用删了,所以
不过呢,直接这样做会导致过程中
具体点:如果存在
然后发现这题的特殊性质:考虑
结合前文,这也就意味着
于是让新加入的
最后还剩个小问题:怎么求出其相应非法点?显然构成区间,我们先看左区间,
又因为
代码
有点抽象,看代码应该能懂。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e5 + 5;
int n, m, _h, x, y;
ll c, ans, k, t[N];
vector<int> h[N];
vector<pair<int, ll> > s[N];
struct DSU{
int fa[N];
inline void init() {for(int i = 1; i < N; ++i) fa[i] = i;}
inline int find(int u) {return fa[u] == u ? u : fa[u] = find(fa[u]);}
}L, R;
inline void upd(int a, ll k) {for(; a < N; a += a & -a) t[a] += k;}
inline ll qry(int a) {ll res = 0; for(; a > 0; a -= a & -a) res += t[a]; return res;}
int main(){
L.init(), R.init();
cin >> n;
for(int i = 1; i <= n; ++i) scanf("%d", &_h), h[_h].push_back(i);
cin >> m;
for(int i = 1; i <= m; ++i) scanf("%d%d%lld", &x, &y, &c), s[y].push_back({x, c});
for(int i = 1; i <= n; ++i){
for(auto star : s[i]){
x = star.first, y = i, c = star.second, k = qry(x);
if(c <= k) ans += c;
else ans += k, upd(L.find(x) + 1, c - k), upd(R.find(x), k - c);
}
for(auto j : h[i]) L.fa[j] = j - 1, R.fa[j] = j + 1;
}
cout << ans;
return 0;
}