zoukankan      html  css  js  c++  java
  • 【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
    " , ans[i]);
    		else puts("NIE");
    	}
    	return 0;
    }
    

     

  • 相关阅读:
    CentOS系统更换软件安装源aliyun的
    判断手机电脑微信 js
    MFC HTTP
    阿里云 镜像 源 debian
    debian root 可以远程登陆
    java-dispose方法
    深入理解JAVA序列化
    Junit单元测试--01
    算法期末考试
    矩阵连乘 动态规划
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7417132.html
Copyright © 2011-2022 走看看