1. 程式人生 > >【bzoj2527】[Poi2011]Meteors 整體二分+樹狀數組

【bzoj2527】[Poi2011]Meteors 整體二分+樹狀數組

string size 單位 mod 直接 mes brush algorithm 成員

題目描述

有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;
}

【bzoj2527】[Poi2011]Meteors 整體二分+樹狀數組