BZOJ 2527 [Poi 2011] 整体二分 解题报告

2527: [Poi2011]Meteors

Description

Byteotian Interstellar Union有N个成员国。现在它发现了一颗新的星球,这颗星球的轨道被分为M份(第M份和第1份相邻),第i份上有第Ai个国家的太空站。
这个星球经常会下陨石雨。BIU已经预测了接下来K场陨石雨的情况。
BIU的第i个成员国希望能够收集Pi单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。

Input

第一行是两个数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单位的陨石样本。

Output

N行。第i行的数Wi表示第i个国家在第Wi波陨石雨之后能够收集到足够的陨石样本。如果到第K波结束后仍然收集不到,输出NIE。
数据范围:
1<=n,m,k<=3*10^5
1<=Pi<=10^9
1<=Ai<10^9

Sample Input

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

Sample Output

3
NIE
1

【解题报告】
来自http://victorwonder.is-programmer.com/posts/70210.html
对于单个查询(假设为第i个国家),我们可以二分k,每次对于一个区间[l,r],手动模拟一下在第mid场流星雨过后,第i个国家一共收集到了多少单位的陨石,如果比pi大,那么答案在[l,mid]范围内,否则答案在[mid+1,r]范围内。
对于多组查询,我们也可以这么做。首先,我们需要用一个列表id[]记录所有查询的编号,刚开始的时候,id[]自然是递增的.同时,我们用一个数组cur[i]记录下,第i个国家在l-1场流星雨过后,收集到的陨石的数目。
主过程为void solve(int head,int tail,int l,int r),表示对于id[head]到id[tail]的所有询问,在[l,r]范围内查询答案,通过上一层的操作,我们保证id[head]到id[tail]的所有询问的答案都在[l,r]范围内。
首先,我们先模拟[l,mid]这么多次操作(在询问重新划分之后,必须要再次模拟,将数组清空),用树状数组或者是线段树计算出在[l,mid]场流星雨之后,每个空间站收集到的陨石的数目。
然后我们查询,每个国家收集到的陨石的数目,要注意的是,我们需要用链表储存每个国家对应的空间站,并且一一枚举,用tmp[id[i]]表示国家id[i]收集到的陨石的数目。
那么从[1,mid]这么多次操作之后,国家id[i]收集到的陨石数目就是tmp[id[i]]+cur[id[i]],如果tmp[id[i]]+cur[id[i]]>p[id[i]],那么表明对于国家id[i],其答案在[l,mid]这个范围内,否则其答案在[mid+1,r]范围内,并将tmp[id[i]]累加到cur[id[i]]上。
还有一个坑点是,tmp[id[i]]可能很大,会爆掉long long,所以如果枚举一个国家的所有空间站的时候,发现tmp[id[i]]已经大于p[id[i]]了,那么就break好了,不然会出错。
因为可能会出现怎么也无法满足的情况,所以我们需要多增加一场流星雨,这场流星雨的数量为infi,保证能够让所有国家都满足要求,那么最后,对于所有答案为k+1的询问,输出NIE就行了。
总的来说,整体二分就是将所有询问一起二分,然后获得每个询问的答案。

注意这道题有一个坑点是会爆long long!!!

代码如下:

/**************************************************************
    Problem: 2527
    User: onepointo
    Language: C++
    Result: Accepted
    Time:16128 ms
    Memory:19572 kb
****************************************************************/

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 300010
#define inf 0x3f3f3f3f
#define l_inf (1LL<<63)-1
#define lowbit(x) (x&(-x))

int n,m,k;
long long t[N];
int ans[N],he[N],ne[N];
struct data {int need,id;long long cur;}a[N],q[N];
struct Rain {int l,r,v;}b[N];

void add(int x,long long v)
{
    if(!x) ++x;
    for(int i=x;i<=m;i+=lowbit(i)) t[i]+=v;
}
void update(int l,int r,long long v)
{
    add(l,v);add(r+1,-v);
}
long long sum(int x)
{
    long long ans=0;
    for(int i=x;i;i-=lowbit(i)) ans+=t[i];
    return ans;     
}
void solve(int l,int r,int h,int t)
{
    if(h>t) return;
    if(l==r)
    {
        for(int i=h;i<=t;i++) ans[a[i].id]=l;
        return;
    }
    int mid=(l+r)>>1;
    for(int i=l;i<=mid;i++)
        if(b[i].l<=b[i].r)
            update(b[i].l,b[i].r,b[i].v);
        else update(1,b[i].r,b[i].v),update(b[i].l,m,b[i].v);
    int t1=h-1,t2=t+1;
    for(int i=h;i<=t;i++)
    {
        long long tmp=0;
        for(int j=he[a[i].id];j;j=ne[j])
        {
            tmp+=sum(j);
            if(a[i].cur+tmp>=a[i].need) break;  //防止爆long long 
        } 
        if(a[i].cur+tmp>=a[i].need) q[++t1]=a[i];
        else a[i].cur+=tmp,q[--t2]=a[i];
    }
    for(int i=h;i<=t;i++) a[i]=q[i];
    for(int i=l;i<=mid;i++) 
        if(b[i].l<=b[i].r) update(b[i].l,b[i].r,-b[i].v);
        else update(1,b[i].r,-b[i].v),update(b[i].l,m,-b[i].v);
    solve(l,mid,h,t1);
    solve(mid+1,r,t2,t);    
}


int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x;
        scanf("%d",&x);
        ne[i]=he[x],he[x]=i;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i].need);
        a[i].id=i;
    }
    scanf("%d",&k);
    for(int i=1;i<=k;i++) scanf("%d%d%d",&b[i].l,&b[i].r,&b[i].v);
    b[++k].l=1;b[k].r=m;b[k].v=inf;
    solve(1,k,1,n);
    for(int i=1;i<=n;i++)
    {
        if(ans[i]==k) printf("NIE\n");
        else printf("%d\n",ans[i]);
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值