Address
Solution
容易想到对于每个询问二分答案,但一次判定是
O
(
m
)
O(m)
O(m) 的。
整体二分的思想就是把所有的询问放在一起二分。
设
s
o
l
v
e
(
l
v
,
r
v
,
l
q
,
r
q
)
solve(l_v,r_v,l_q,r_q)
solve(lv,rv,lq,rq) 表示答案范围为
[
l
v
,
r
v
]
[l_v,r_v]
[lv,rv] ,处理第
l
q
l_q
lq 个询问到第
r
q
r_q
rq 个询问。
(1)取
m
i
d
v
=
⌊
l
v
+
r
v
2
⌋
mid_v=\lfloor\frac{l_v+r_v}2\rfloor
midv=⌊2lv+rv⌋ 。
(2)用一个树状数组维护每一个位置下了多少场陨石雨。将第
l
v
l_v
lv 场陨石雨到第
m
i
d
v
mid_v
midv 场陨石雨全部加入树状数组。
(3)按顺序考虑每个询问,把询问分成两类:
①在
[
l
v
,
m
i
d
v
]
[l_v,mid_v]
[lv,midv] 内可以收集够陨石。
②在
[
l
v
,
m
i
d
v
]
[l_v,mid_v]
[lv,midv] 内没收集到足够的陨石。
把询问分成两组,
[
l
q
,
m
i
d
q
]
[l_q,mid_q]
[lq,midq] 为①类,
[
m
i
d
q
+
1
,
r
q
]
[mid_q+1,r_q]
[midq+1,rq] 为②类。
如果一个②类询问是国家
i
i
i ,且树状数组上查到在
[
l
v
,
m
i
d
v
]
[l_v,mid_v]
[lv,midv] 内国家
i
i
i 收集到的陨石数为
Z
Z
Z ,则把
W
i
W_i
Wi 减掉
Z
Z
Z 。
(4)递归到
s
o
l
v
e
(
l
v
,
m
i
d
v
,
l
q
,
m
i
d
q
)
solve(l_v,mid_v,l_q,mid_q)
solve(lv,midv,lq,midq) 和
s
o
l
v
e
(
m
i
d
v
+
1
,
r
v
,
m
i
d
q
+
1
,
r
q
)
solve(mid_v+1,r_v,mid_q+1,r_q)
solve(midv+1,rv,midq+1,rq) 。
时间复杂度
O
(
(
n
+
m
+
K
)
log
K
log
m
)
O((n+m+K)\log K\log m)
O((n+m+K)logKlogm) 。
Code
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Bitr(x, n) for (; x <= n; x += x & -x)
#define Bitl(x) for (; x; x -= x & -x)
using namespace std;
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 3e5 + 5;
int n, m, o[N], p[N], K, l[N], r[N], a[N], q[N], q1[N], q2[N], ans[N];
ll A[N];
vector<int> num[N];
void clean(int x)
{
Bitr(x, m) A[x] = 0;
}
void change(int x, int v)
{
Bitr(x, m) A[x] += v;
}
ll ask(int x)
{
ll res = 0;
Bitl(x) res += A[x];
return res;
}
void interv(int l, int r, int v)
{
change(l, v); change(r + 1, -v);
}
void cirv(int l, int r, int v)
{
if (l <= r) interv(l, r, v);
else interv(l, m, v), interv(1, r, v);
}
void cleanv(int l, int r)
{
clean(l); clean(r + 1);
}
void cleancir(int l, int r)
{
if (l <= r) cleanv(l, r);
else cleanv(l, m), cleanv(1, r);
}
void jiejuediao(int lval, int rval, int lq, int rq)
{
if (lval > rval || lq > rq) return;
int i, j, mid = lval + rval >> 1, tot1 = 0, tot2 = 0, tot = lq - 1;
For (i, lval, mid) cirv(l[i], r[i], a[i]);
For (i, lq, rq)
{
int j, z = num[q[i]].size(); ll tmp = 0;
For (j, 0, z - 1)
{
tmp += ask(num[q[i]][j]);
if (p[q[i]] <= tmp) break;
}
if (p[q[i]] <= tmp) ans[q[i]] = mid, q1[++tot1] = q[i];
else p[q[i]] -= tmp, q2[++tot2] = q[i];
}
For (i, 1, tot1) q[++tot] = q1[i];
For (i, 1, tot2) q[++tot] = q2[i];
For (i, lval, mid) cleancir(l[i], r[i]);
jiejuediao(lval, mid - 1, lq, lq + tot1 - 1);
jiejuediao(mid + 1, rval, lq + tot1, rq);
}
int main()
{
int i;
n = read(); m = read();
For (i, 1, m) num[read()].push_back(i);
For (i, 1, n) p[q[i] = i] = read();
K = read();
For (i, 1, n) ans[i] = K + 1;
For (i, 1, K) l[i] = read(), r[i] = read(), a[i] = read();
jiejuediao(1, K, 1, n);
For (i, 1, n)
if (ans[i] == K + 1) puts("NIE");
else printf("%d\n", ans[i]);
return 0;
}