嘟嘟嘟


做了几道题之后,对整体二分有点感觉了。


整体二分的本质就是二分答案。所以这道题二分的就是次数。
然后就是套路了,把小于\(mid\)的操作都添加减去,然后查询,如果查询的值\(x\)比给定值大,就把这个询问放到左区间,否则减去\(x\),放到右区间。
具体的操作,要支持区间加和单点查,第一反应是线段树,结果有两个点TLE的很惨。所以改成了树状数组差分维护前缀和,竟然过了,而且还跑的特别块。常数真是致命啊……

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define rg register
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 3e5 + 5;
inline ll read()
{
  ll ans = 0;
  char ch = getchar(), last = ' ';
  while(!isdigit(ch)) last = ch, ch = getchar();
  while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
  if(last == '-') ans = -ans;
  return ans;
}
inline void write(ll x)
{
  if(x < 0) x = -x, putchar('-');
  if(x >= 10) write(x / 10);
  putchar(x % 10 + '0');
}

int n, m, k, val[maxn];
vector<int> con[maxn];
struct Node
{
  int L, R, w, id;
}t[maxn << 1], tl[maxn << 1], tr[maxn << 1];
int ans[maxn];

ll c[maxn];
int lowbit(int x) {return x & -x;}
inline void clear(int pos)
{
  for(; pos <= m; pos += lowbit(pos))
    if(c[pos]) c[pos] = 0;
    else break;
}
inline void add(int pos, int d)
{
  if(!pos) return;
  for(; pos <= m; pos += lowbit(pos)) c[pos] += d;
}
inline ll query(int pos)
{
  ll ret = 0;
  for(; pos; pos -= lowbit(pos)) ret += c[pos];
  return ret;
}

inline void Change(Node q)
{
  if(q.L > q.R) add(q.L, q.w), add(1, q.w), add(q.R + 1, -q.w);
  else add(q.L, q.w), add(q.R + 1, -q.w);
}
inline void Clear(Node q)
{
  if(q.L > q.R) clear(q.L), clear(1), clear(q.R + 1);
  else clear(q.L), clear(q.R + 1);
}

inline void solve(int kl, int kr, int ql, int qr)
{
  if(ql > qr) return;
  if(kl == kr)
    {
      for(int i = ql; i <= qr; ++i)
	if(t[i].id > k + 1) ans[t[i].id - k - 1] = kl;
      return;
    }
  int mid = (kl + kr) >> 1, id1 = 0, id2 = 0;
  for(int i = ql; i <= qr; ++i)
    {
      if(t[i].id <= k + 1)
	{
	  if(t[i].id <= mid) Change(t[i]), tl[++id1] = t[i];
	  else tr[++id2] = t[i];
	}
      else
	{
	  ll tot = 0; int x = t[i].id - k - 1;
	  for(int j = 0; j < (int)con[x].size(); ++j)
	    if((tot += query(con[x][j])) >= t[i].w) break;
	  if(tot >= t[i].w) tl[++id1] = t[i];
	  else t[i].w -= tot, tr[++id2] = t[i];
	}
    }
  for(int i = 1; i <= id1; ++i) if(tl[i].id <= k + 1 && tl[i].id <= mid) Clear(tl[i]);
  for(int i = 1; i <= id1; ++i) t[ql + i - 1] = tl[i];
  for(int i = 1; i <= id2; ++i) t[ql + id1 + i - 1] = tr[i];
  solve(kl, mid, ql, ql + id1 - 1);
  solve(mid + 1, kr, ql + id1, qr);
}

int main()
{
  n = read(); m = read();
  for(int i = 1, x; i <= m; ++i) x = read(), con[x].push_back(i);
  for(int i = 1; i <= n; ++i) val[i] = read();
  k = read();
  for(int i = 1; i <= k; ++i)
      t[i].L = read(), t[i].R = read(), t[i].w = read(), t[i].id = i;
  t[k + 1] = (Node){1, m, INF, k + 1};
  for(int i = 1; i <= n; ++i) t[k + 1 + i] = (Node){0, 0, val[i], k + 1 + i};
  solve(1, k + 1, 1, k + n + 1);
  for(int i = 1; i <= n; ++i)
    if(ans[i] > k) puts("NIE");
    else write(ans[i]), enter;
  return 0;
}