P1315题解
不是你们都是怎么贪心的?我怎么一个都看不懂?
不难发现,答案为以下式子:
其中,
我们再来推一波
其中
再推
发现这是一个递推式,拆开得到:
通过瞪眼法不难得到最终的式子是:
把
再将式子合并到答案的式子中:
然后他们就开始贪心了。但是我没看懂,所以我只能分析这个式子。
我们现在能修改的是从
观察
现在很清楚了,我们只需要选在
这时再贪心的想,我们对
一个线段被修改是不是优的,就取决于它除了左端点以外的点的
然后就是修改时的细节了。修改一个线段后,由于不会修改线段的左端点,线段中有一个点(不是右端点,因为右端点本来就比左端点大)可能比左端点大,那么我们就在那个点对线段分割。而当线段左端点的
具体实现看代码吧。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MAXN = 5e6+5;
inline ll max(ll a, ll b) { return a > b ? a : b; }
struct Node
{
ll maxn; int maxpos; ll lazy;
Node friend operator+(const Node &a, const Node &b)
{
Node res;
res.maxpos = (a.maxn >= b.maxn ? a.maxpos : b.maxpos);
res.maxn = max(a.maxn, b.maxn);
res.lazy = 0;
return res;
}
};
struct Pas
{
int tim, l, r;
} pas[MAXN];
int la[MAXN];
int pre[MAXN];
int d[MAXN];
int cnt[MAXN];
ll sum[MAXN];
int n, m, k;
ll T;
struct SegmentTree
{
#define lp (p<<1)
#define rp (p<<1|1)
Node tree[MAXN<<2];
void push_up(int p)
{
tree[p] = tree[lp] + tree[rp];
}
void push_down(int p)
{
if(!tree[p].lazy) return;
tree[lp].lazy += tree[p].lazy;
tree[rp].lazy += tree[p].lazy;
tree[lp].maxn += tree[p].lazy;
tree[rp].maxn += tree[p].lazy;
tree[p].lazy = 0;
}
void build(int s, int t, int p)
{
if(s == t)
{
tree[p].maxn = la[s] - pre[s];
tree[p].maxpos = s;
return;
}
int mid = (s + t) >> 1;
build(s, mid, lp);
build(mid+1, t, rp);
push_up(p);
}
void add(int l, int r, int s, int t, int p, int x)
{
if(l <= s && t <= r)
{
tree[p].maxn += x;
tree[p].lazy += x;
return;
}
int mid = (s + t) >> 1;
push_down(p);
if(l <= mid) add(l, r, s, mid, lp, x);
if(r > mid) add(l, r, mid+1, t, rp, x);
push_up(p);
}
Node query(int l, int r, int s, int t, int p)
{
if(l <= s && t <= r) return tree[p];
int mid = (s + t) >> 1;
push_down(p);
if(r <= mid) return query(l, r, s, mid, lp);
if(l > mid) return query(l, r, mid+1, t, rp);
return query(l, r, s, mid, lp) + query(l, r, mid+1, t, rp);
}
}tree;
struct seg
{
int l, r; // 左右端点
ll sum, lmax; // cnt 的和,原始左端点位置
seg(int _l = 0, int _r = 0, ll _sum = 0, ll _vis = 0) : l(_l), r(_r), sum(_sum), lmax(_vis) {}
bool operator<(const seg &_) const { return sum < _.sum; }
};
priority_queue<seg> q;
int main()
{
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i < n; i++) scanf("%d", &d[i]), pre[i+1] = pre[i] + d[i];
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d", &pas[i].tim, &pas[i].l, &pas[i].r);
la[pas[i].l] = max(la[pas[i].l], pas[i].tim);
cnt[pas[i].r]++;
T += pas[i].tim;
}
tree.build(1, n, 1);
for(int i = 1; i <= n+1; i++) sum[i] = sum[i-1] + cnt[i];
for(int i = 2, lst = 1, lstv = la[1] - pre[1]; i <= n; i++)
{
if(la[i] - pre[i] >= lstv)
{
q.emplace(seg(lst, i, sum[i] - sum[lst], lst));
lst = i;
lstv = la[i] - pre[i];
}
if(i == n && lst != n) q.emplace(seg(lst, i+1, sum[i] - sum[lst], lst));
}
for(; k && !q.empty(); )
{
seg cur = q.top(); q.pop();
if(cur.l == cur.r-1) // 特判一下
{
if(k <= d[cur.l]) tree.add(cur.l+1, n, 1, n, 1, k), d[cur.l] -= k, k = 0;
else k -= d[cur.l], tree.add(cur.l+1, n, 1, n, 1, d[cur.l]), d[cur.l] = 0;
continue;
}
ll lmax = max(tree.query(cur.l, cur.l, 1, n, 1).maxn, tree.query(cur.lmax, cur.lmax, 1, n, 1).maxn);
ll maxn = tree.query(cur.l+1, cur.r-1, 1, n, 1).maxn;
if(k > min((ll)d[cur.l], lmax - maxn))
{
tree.add(cur.l+1, n, 1, n, 1, min((ll)d[cur.l], lmax - maxn));
k -= min((ll)d[cur.l], lmax - maxn);
if(d[cur.l] > lmax - maxn)
{
int pos = tree.query(cur.l+1, cur.r-1, 1, n, 1).maxpos;
if(cur.l < pos) q.emplace(seg(cur.l, pos, sum[pos] - sum[cur.l], cur.lmax));
if(pos < cur.r) q.emplace(seg{pos, cur.r, sum[cur.r] - sum[pos], pos});
}
else if(cur.l+1 < cur.r) q.emplace(seg(cur.l+1, cur.r, sum[cur.r] - sum[cur.l+1], cur.lmax));
d[cur.l] -= min((ll)d[cur.l], lmax - maxn);
continue;
}
tree.add(cur.l+1, n, 1, n, 1, k);
d[cur.l] -= k;
k = 0;
}
for(int i = 1; i < n; i++) pre[i+1] = pre[i] + d[i];
ll ans = -T;
for(int i = 2; i <= n; i++)
{
ans += cnt[i] * (tree.query(1, i-1, 1, n, 1).maxn + pre[i]);
}
printf("%lld\n", ans);
return 0;
}