题解 P3527 【[POI2011]MET-Meteors】
Juan_feng
2019-08-25 21:54:47
小蒟蒻Jf写水题划水遭到毒瘤怒斥QAQAQ 再也不敢了.jpg
~~题解还是要水一发的~~
## 题是整体二分题, 这里提供一个分块做法qwq
**思路如下:**
对操作(区间加)进行分块, 设每T个操作为一组, 每次加入一组的操作, 在序列上差分一下求出每个位置当前的答案。 然后扫一下所有的国家(国家用vector保存这个国家的每一个位置), 如果这个国家的答案已经被求出来了, 那么直接跳过这个国家; 否则就判断一下这个国家所有位置的答案之和是否大于需求, 如果大于则说明使其达成需求的操作一定在这一组里面(不超过T个), 这时候倒着扫一下就可以了。
因为每个位置最多只会被倒着扫一次, 没次不会扫超过T个操作, 且一共要做n/T次差分。 所以当T取sqrt(n)时, 总复杂度为m sqrt(n) + n sqrt(m)。
然后就没什么啦qwq,有什么问题可以私信小蒟蒻Jf。
**那么代码如下**:
```
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <vector>
#define maxn 300010
#define re register
#define FOR(i, l, r) for(re int i = l; i <= r; ++i)
#define DFR(i, l, r) for(re int i = l; i >= r; --i)
using namespace std;
int n, m, c, r, t, x, y, z, k;
int sq;
int a[maxn], ned[maxn], bl[maxn], siz[maxn], of[maxn], anss[maxn];
long long qwq[maxn], cha[maxn], res[maxn];
vector<int> ve[maxn];
struct hz {
int l, r, k;
}q[maxn];
int main() {
scanf("%d%d", &n, &m);
FOR(i, 1, m) {
scanf("%d", &t);
ve[t].push_back(i),
++siz[t],
of[i] = t;
}
FOR(i, 1, n)
scanf("%d", &ned[i]);
scanf("%d", &k);
sq = sqrt(k)*2.5; //可调参
FOR(i, 1, (k-1)/sq+1) {
int l = (i-1)*sq+1, r = min(k, i*sq);
FOR(j, l, r) {
scanf("%d%d%d", &q[j].l, &q[j].r, &q[j].k);
if(q[j].l > q[j].r)
qwq[q[j].l] += q[j].k, qwq[m+1] -= q[j].k, qwq[1] += q[j].k, qwq[q[j].r+1] -= q[j].k;
else
qwq[q[j].l] += q[j].k, qwq[q[j].r+1] -= q[j].k;
}
FOR(j, 1, m) {
cha[j] = qwq[j];
cha[j] += cha[j-1];
res[of[j]] += cha[j];
}
FOR(u, 1, n) {
long long pt = res[u];
if(anss[u] || pt < ned[u])
continue;
DFR(j, r, l) {
FOR(xx, 0, siz[u]-1)
if(q[j].l <= q[j].r) {
if(q[j].l <= ve[u][xx] && ve[u][xx] <= q[j].r)
pt -= q[j].k;
}
else
if(q[j].l <= ve[u][xx] || ve[u][xx] <= q[j].r)
pt -= q[j].k;
if(pt < ned[u]) {
anss[u] = j;
break;
}
}
}
memset(res, 0, sizeof(res));
}
FOR(i, 1, n)
if(anss[i] == 0)
puts("NIE");
else
printf("%d\n", anss[i]);
}
```