1. 程式人生 > >[BZOJ2527][POI2011]Meteors(整體二分+樹狀陣列)

[BZOJ2527][POI2011]Meteors(整體二分+樹狀陣列)

Address

洛谷P3527
BZOJ2527
LOJ#2169

Solution

容易想到對於每個詢問二分答案,但一次判定是 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)
表示答案範圍為 [ l v , r v ] [l_v,r_v] ,處理第 l q l_q 個詢問到第 r q r_q 個詢問。
(1)取 m i d v = l v + r v 2 mid_v=\lfloor\frac{l_v+r_v}2\rfloor
(2)用一個樹狀陣列維護每一個位置下了多少場隕石雨。將第 l v l_v 場隕石雨到第 m i d v mid_v 場隕石雨全部加入樹狀陣列。
(3)按順序考慮每個詢問,把詢問分成兩類:
①在 [ l v , m i d v ] [l_v,mid_v] 內可以收集夠隕石。
②在 [ l v , m i d v ] [l_v,mid_v] 內沒收集到足夠的隕石。
把詢問分成兩組, [ l q , m i d q ] [l_q,mid_q] 為①類, [ m i d q + 1 , r q ] [mid_q+1,r_q] 為②類。
如果一個②類詢問是國家 i i ,且樹狀陣列上查到在 [ l v , m i d v ] [l_v,mid_v] 內國家 i i 收集到的隕石數為 Z Z ,則把 W i W_i 減掉 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) 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)
時間複雜度 O ( ( n + m + K ) log K log m ) O((n+m+K)\log K\log m)

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] =