【bzoj2527】[Poi2011]Meteors 整体二分+树状数组

题目描述

有N个成员国。现在它发现了一颗新的星球,这颗星球的轨道被分为M份(第M份和第1份相邻),第i份上有第Ai个国家的太空站。

这个星球经常会下陨石雨。BIU已经预测了接下来K场陨石雨的情况。BIU的第i个成员国希望能够收集Pi单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。

输入

第一行是两个数N,M。

第二行有M个数,第i个数Oi表示第i段轨道上有第Oi个国家的太空站。

第三行有N个数,第i个数Pi表示第i个国家希望收集的陨石数量。

第四行有一个数K,表示BIU预测了接下来的K场陨石雨。

接下来K行,每行有三个数Li,Ri,Ai,表示第K场陨石雨的发生地点在从Li顺时针到Ri的区间中(如果Li<=Ri,就是Li,Li+1,...,Ri,否则就是Ri,Ri+1,...,m-1,m,1,...,Li),向区间中的每个太空站提供Ai单位的陨石样本。

输出

N行。第i行的数Wi表示第i个国家在第Wi波陨石雨之后能够收集到足够的陨石样本。如果到第K波结束后仍然收集不到,输出NIE。

样例输入

3 5
1 3 2 1 3
10 5 7
3
4 2 4
1 3 1
3 5 2

样例输出

3
NIE
1


题解

整体二分+树状数组

答案满足二分性质,所以考虑将询问离线下来,然后整体二分解决。

令$solve(b,e,l,r)$表示处理询问下标区间为[b,e],修改的区间为[l,r]的答案。

那么l=r时直接答案为l。

当l≠r时,先处理[l,mid]的修改,然后统计[b,e]内每个国家得到的数量,判断是否小于p。可以把所有空间站挂链,然后把每个点上的加起来。这个过程区间修改单点查询,可以使用树状数组。

有一个小trick:可以把修改区间设置为[1,k+1],这样当某个答案为k+1时,说明k个不能满足,输出-1.

然而最恶心的是:本题爆long long!

考虑:300000个空间站属于1个国家,300000次修改,每次修改加到[1,300000]上,加上10^9,这样乘起来会爆long long的2^63-1。

于是被迫改成中精度,把每次的数对10^15取模,最后判断时结合着高位和低位一起看即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 300010
using namespace std;
typedef long long ll;
const ll mod = 1000000000000000ll;
ll w[N] , v[N] , f[N];
int m , a[N] , t[N] , lp[N] , rp[N] , head[N] , next[N] , ans[N];
void add(int x , ll a)
{
	int i;
	for(i = x ; i <= m ; i += i & -i) f[i] += a;
}
ll query(int x)
{
	int i;
	ll ans = 0;
	for(i = x ; i ; i -= i & -i) ans += f[i];
	return ans;
}
void solve(int b , int e , int l , int r)
{
	int mid = (l + r) >> 1 , i , j , tl = b , tr = e;
	ll c , vc;
	if(l == r)
	{
		for(i = b ; i <= e ; i ++ ) ans[a[i]] = l;
		return;
	}
	for(i = l ; i <= mid ; i ++ )
	{
		if(lp[i] <= rp[i]) add(lp[i] , v[i]) , add(rp[i] + 1 , -v[i]);
		else add(lp[i] , v[i]) , add(m + 1 , -v[i]) , add(1 , v[i]) , add(rp[i] + 1 , -v[i]);
	}
	for(i = b ; i <= e ; i ++ )
	{
		if(!w[a[i]]) t[tl ++ ] = a[i];
		else
		{
			for(c = vc = 0 , j = head[a[i]] ; j ; j = next[j])
			{
				c += query(j);
				if(c >= mod) vc += c / mod , c %= mod;
				else if(c < 0) vc += c / mod - 1 , c = (c % mod + mod) % mod;
			}
			if(vc || c >= w[a[i]]) t[tl ++ ] = a[i];
			else w[a[i]] -= c , t[tr -- ] = a[i];
		}
	}
	for(i = b ; i <= e ; i ++ ) a[i] = t[i];
	for(i = l ; i <= mid ; i ++ )
	{
		if(lp[i] <= rp[i]) add(lp[i] , -v[i]) , add(rp[i] + 1 , v[i]);
		else add(lp[i] , -v[i]) , add(m + 1 , v[i]) , add(1 , -v[i]) , add(rp[i] + 1 , v[i]);
	}
	solve(b , tr , l , mid) , solve(tl , e , mid + 1 , r);
}
int main()
{
	int n , i , x , k;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i <= m ; i ++ ) scanf("%d" , &x) , next[i] = head[x] , head[x] = i;
	for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &w[i]) , a[i] = i;
	scanf("%d" , &k);
	for(i = 1 ; i <= k ; i ++ ) scanf("%d%d%lld" , &lp[i] , &rp[i] , &v[i]);
	solve(1 , n , 1 , k + 1);
	for(i = 1 ; i <= n ; i ++ )
	{
		if(ans[i] >= 1 && ans[i] <= k) printf("%d\n" , ans[i]);
		else puts("NIE");
	}
	return 0;
}

 

 

posted @ 2017-08-23 11:02  GXZlegend  阅读(308)  评论(2编辑  收藏  举报