【bzoj2527】[Poi2011]Meteors 整体二分+树状数组
题目描述
这个星球经常会下陨石雨。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; }