P7219 反贪做法证明
lihaochen123 · · 题解
反贪真是个美妙的东西,可惜目前没有找到任何一个靠谱的证明。
@small_lemon_qwq 给出的 GPT 证明,由我验证了正确性,简化了表述然后放到了这里。
直接贪心(假的):从下往上扫,每次看这个点会和当前最优解中的哪些点冲突,如果删掉这个点代价更小就删掉这个点,否则删掉当前最优解中和它冲突的所有点。
反悔贪心:这篇题解做法。具体来讲,按照
证明:
首先
引理
构造冲突图
我们设
定理
定理
定理 Meow:设
证明:
首先设
显然有
假设
定理 Final:答案为
综上我们证明了反贪做法的正确性。
:::info[rec&code]
rec。
#include <cstdio>
#include <vector>
#include <bitset>
#include <algorithm>
using namespace std;
bitset<200005> bs;
class star
{
public:
int x, y, c;
} stars[200005];
class dsu
{
public:
int fa[200005], rk[200005];
int getfa(int x) { while(x != fa[x]) x = fa[x] = fa[fa[x]]; return x; }
void merge(int x, int y) { if((x = getfa(x)) == (y = getfa(y))) return; if(rk[x] < rk[y]) fa[x] = y; else if(rk[x] > rk[y]) fa[y] = x; else { fa[x] = y; rk[y]++; }}
} dk;
int vl[200005], vr[200005];
vector<int> ktop[200005];
class fenwick
{
public:
long long st[200005];
void vadd(int pos, long long val, int n) { do { st[pos] += val; } while((pos += pos & -pos) <= n); }
long long qsum(int pos) { long long sum = 0; do { sum += st[pos]; } while(pos -= pos & -pos); return sum; }
} fw;
int main()
{
int n;
scanf("%d", &n);
for(int i=1;i<=n;i++)
{
dk.fa[i] = i;
vl[i] = i+1;
vr[i] = i-1;
int a;
scanf("%d", &a);
ktop[a].push_back(i);
}
vl[0] = 1; vr[n+1] = n; dk.fa[n+1] = n+1;
int m;
scanf("%d", &m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d", &stars[i].x, &stars[i].y, &stars[i].c);
}
sort(stars + 1, stars + m + 1, [](const auto &x, const auto &y) { return x.y < y.y; });
int cur = 0;
long long sum = 0;
for(int i=1;i<=m;i++)
{
while(cur + 1 < stars[i].y)
{
for(int x : ktop[++cur])
{
int ll = vl[dk.getfa(x-1)], rr = vr[dk.getfa(x+1)];
if(bs[x-1]) dk.merge(x, x-1);
if(bs[x+1]) dk.merge(x, x+1);
bs[x] = true;
vl[dk.getfa(x)] = ll; vr[dk.getfa(x)] = rr;
}
}
int gr = vr[dk.getfa(stars[i].x)], gl = vl[dk.getfa(stars[i].x)];
long long w = fw.qsum(stars[i].x);
long long d = max(0ll, stars[i].c - w);
sum += stars[i].c - d;
fw.vadd(gl, d, n); fw.vadd(gr + 1, -d, n);
}
printf("%lld\n", sum);
return 0;
} // luogu://article/rrie56rp
:::