题解 【P3527 [POI2011]MET-Meteors】
题意
一个环形被分为 \(m\) 份,每份都属于 \(n\) 个国家之一,有 \(k\) 场陨石,每场对应环上一段区间,为此区间上每份的隶属国家增加 \(a_i\) 的贡献,每个国家期望得到 \(p_i\) 的陨石,问第几波陨石过后能够达成目标?
\(n,m,k\le 3e5\)
思路
因为答案具有可二分性,即可以二分在第几波陨石达成目标,因为有修改,一个一个国家二分显得有点浪费,可以联想到整体二分,二分所有国家的完成时间,而修改操作可以由树状数组(区间修改,单点查询)来实现,注意要在最后加一波无限大的陨石特判 \(\texttt{NIE}\) 的情况。
对于查询,直接用树状数组暴力一个一个查询当前块内所有国家的所有环形上的区域。
时间复杂度为 \(O(N\log^2N)\) 。
以下给出时间复杂度的证明。
证明
主要时间复杂度产生在修改和查询两种操作上。
注意 \(n,m,k\) 同阶。
对于修改:第 \(i\) 场陨石在整理二分中是从 \([1,k+1]\) 这个区间到 \([i,i]\) 这个区间,一共约有 \(\log_2 k\) 层,每层都对其执行两遍(一遍修改,一遍清除)修改操作,修改操作复杂度约为 \(O(\log_2 m)\) ,所以总复杂度约为 \(O(N\log^2N)\) 。
对于查询:看起来是暴力一个一个查询,但是对于环形上每个区域,也都是在 \(\log_2 k\) 层上每层都都做一次 \(O(\log_2 m)\) 的查询,所以复杂度也是 \(O(N\log^2N)\) 。
代码
int a, b, c, p[N + 10], s[N + 10], ans[N + 10];
vector<int> st[N + 10], Q, L, R;
struct node
{
int opt, id, x, y, z;
} ask[N + 10];
inline void add(int n, int k)
{
if (!n)
return;
for (; n <= b; n += n & -n)
p[n] += k;
}
inline int query(int n)
{
int res = 0;
for (; n; n -= n & -n)
res += p[n];
return res;
}
inline void change(int l, int r, int val)
{
if (l <= r)
add(l, val), add(r + 1, -val);
else
add(1, val), add(r + 1, -val), add(l, val);
}
inline void solve(int l, int r)
{
if (l == r)
{
for (int i = 0; i < Q.size(); i++)
if (ask[Q[i]].opt == 2)
ans[ask[Q[i]].id] = l;
return;
}
int mid = (l + r) >> 1;
for (int i = 0; i < Q.size(); i++)
{
int n = Q[i];
if (ask[n].opt == 1)
{
if (ask[n].id <= mid)
change(ask[n].x, ask[n].y, ask[n].z), L.PB(n);
else
R.PB(n);
}
else
{
int tmp = 0;
for (int j = 0; j < st[ask[n].id].size(); j++)
{
int m = st[ask[n].id][j];
tmp += query(m);
if (tmp >= s[ask[n].id])
break;
}
if (s[ask[n].id] <= tmp)
L.PB(n);
else
R.PB(n), s[ask[n].id] -= tmp;
}
}
for (int i = 0; i < L.size(); i++)
{
int n = L[i];
if (ask[n].opt != 1)
continue;
change(ask[n].x, ask[n].y, -ask[n].z);
}
vector<int> tmp = R;
Q = L;
R.clear();
L.clear();
solve(l, mid);
Q = tmp;
solve(mid + 1, r);
}
signed main()
{
// freopen("in1.in", "r", stdin);
a = read();
b = read();
for (int i = 1; i <= b; i++)
st[read()].PB(i);
for (int i = 1; i <= a; i++)
s[i] = read();
c = read();
for (int i = 1; i <= c; i++)
{
ask[i].opt = 1;
ask[i].id = i;
ask[i].x = read();
ask[i].y = read();
ask[i].z = read();
}
ask[++c].opt = 1;
ask[c].id = c;
ask[c].x = 1;
ask[c].y = b;
ask[c].z = 1e9;
for (int i = 1; i <= a; i++)
ask[i + c].opt = 2, ask[i + c].id = i;
c += a;
for (int i = 1; i <= c; i++)
Q.PB(i);
solve(1, c - a);
for (int i = 1; i <= a; i++)
if (ans[i] != c - a)
printf("%d\n", ans[i]);
else
puts("NIE");
return 0;
}