【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; }
【bzoj2527】[Poi2011]Meteors 整體二分+樹狀數組