[BZOJ2527][POI2011]Meteors(整体二分+树状数组)

Address

洛谷P3527
BZOJ2527
LOJ#2169

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值