第九届河北省大学生程序设计竞赛 L.通信拦截
难蚌,我以为签到呢,结果出题人说是次难题,当时 VP 看这题估计不过半小时就把这题做出来了。
不知道题解巴拉巴拉说什么线段树什么的,显然这题不需要这么高级的算法。
注意到 \(x\) 对一个线段 \([l, \, r]\) 的贡献,在前半段可以发现是一个等差数列加上一个全部相等的一段,贡献为
\[\frac{(x - l)(x - l + 1)}{2} + (r - (2l - x))(l - x) = \frac{-3x^2 + (2r + 4l + 1)x - l(2r + l + 1)}{2}
\]
在右半段也有个类似的贡献,是等差数列取不完的贡献
\[\frac{x^2 - (2r + 1)x + r(r + 1)}{2}
\]
然后放两个堆出来维护一下左区间和右区间的贡献,我们就可以知道总贡献。
时间复杂度 \(O(n\log{n})\)。
#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
typedef pair<ll, ll> PII;
const int N = 2e5 + 10;
int n;
struct Segment {
ll l, r;
}a[N];
bool cmp(Segment x, Segment y) {
if (x.l == y.l) return x.r < y.r;
return x.l < y.l;
}
ll calcL1(int x) {return 2 * a[x].r + 4 * a[x].l + 1;}
ll calcL2(int x) {return -a[x].l * (2 * a[x].r + a[x].l + 1);}
ll calcR1(int x) {return -(2 * a[x].r + 1);}
ll calcR2(int x) {return a[x].r * (a[x].r + 1);}
void solve() {
cin >> n;
for (int i = 1; i <= n; i ++ ) {
int x; cin >> x;
a[i] = {i, min(i + x, n)};
}
sort(a + 1, a + n + 1, cmp);
int cnt = 1;
ll Lc1 = 0, Lc2 = 0, Rc1 = 0, Rc2 = 0;
priority_queue<PII, vector<PII>, greater<PII>> Lseg, Rseg;
for (ll i = 1; i <= n; i ++ ) {
while (cnt <= n && a[cnt].l == i) {
Lseg.push({a[cnt].l + a[cnt].r >> 1, cnt});
Lc1 += calcL1(cnt);
Lc2 += calcL2(cnt);
cnt ++ ;
}
while (Lseg.size() && Lseg.top().first < i) {
int id = Lseg.top().second; Lseg.pop();
Rseg.push({a[id].r, id});
Lc1 -= calcL1(id);
Lc2 -= calcL2(id);
Rc1 += calcR1(id);
Rc2 += calcR2(id);
}
while (Rseg.size() && Rseg.top().first < i) {
int id = Rseg.top().second; Rseg.pop();
Rc1 -= calcR1(id);
Rc2 -= calcR2(id);
}
cout << (-3 * i * i * (ll)Lseg.size() + Lc1 * i + Lc2) / 2 + (i * i * (ll)Rseg.size() + Rc1 * i + Rc2) / 2 << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
// cin>>T;
while (T -- ) solve();
return 0;
}

浙公网安备 33010602011771号