[bzoj 2527--POI2011]Meteors

124 篇文章 2 订阅
2 篇文章 0 订阅

Byteotian Interstellar Union有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。

这道题答案很明显满足二分性,但是是多个询问,所以要整体二分。建议大家学过整体二分之后再来捉此题。那之后难点就变成如何check了,那很明显是树状数组嘛,改段求点,每个国家暴力判断是否满足就可以了。NIE的情况只要上限+1,之后如果答案为上限+1,那就是NIE了,最后记得开long long,wy在累加过程中有可能爆long long,所以要边加边判断。那这题就解决了。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
    int l,r,d;long long k;
}b[310000],q[310000],t[310000];
struct bian
{
    int x,y,next;
}a[610000];int len,last[310000];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int n,m;
long long s[310000];
int lowbit(int x){return x&-x;}
void add(int x,long long k)
{
    while(x<=m)
    {
        s[x]+=k;
        x+=lowbit(x);
    }
}
long long getsum(int x)
{
    long long sum=0;
    while(x>=1)
    {
        sum+=s[x];
        x-=lowbit(x);
    }
    return sum;
}
int ans[310000];
void solve(int l,int r,int sl,int sr)
{
    if(sl==sr)
    {
        for(int i=l;i<=r;i++)ans[b[i].d]=sl;
        return ;
    }
    int mid=(sl+sr)/2;
    for(int i=sl;i<=mid;i++)
    {
        if(q[i].l<=q[i].r)add(q[i].l,q[i].k),add(q[i].r+1,-q[i].k);
        else add(q[i].l,q[i].k),add(1,q[i].k),add(q[i].r+1,-q[i].k);
    }
    int stl=l,str=r;
    for(int i=l;i<=r;i++)
    {
        long long wy=0;int x=b[i].d;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            wy+=getsum(y);
            if(wy>=b[i].k)break;
        }
        if(wy>=b[i].k)t[stl++]=b[i];
        else 
        {
            b[i].k-=wy;
            t[str--]=b[i];
        }
    }
    for(int i=sl;i<=mid;i++)
    {
        if(q[i].l<=q[i].r)add(q[i].l,-q[i].k),add(q[i].r+1,q[i].k);
        else add(q[i].l,-q[i].k),add(1,-q[i].k),add(q[i].r+1,q[i].k);
    }
    for(int i=l;i<=r;i++)b[i]=t[i];
    if(stl!=l)solve(l,stl-1,sl,mid);
    if(str!=r)solve(str+1,r,mid+1,sr);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x;
        scanf("%d",&x);
        ins(x,i);
    }
    for(int i=1;i<=n;i++)scanf("%lld",&b[i].k),b[i].d=i;
    int kk;
    scanf("%d",&kk);
    for(int i=1;i<=kk;i++)scanf("%d%d%lld",&q[i].l,&q[i].r,&q[i].k);
    solve(1,n,1,kk+1);
    for(int i=1;i<=n;i++)
    {
        if(ans[i]==kk+1)printf("NIE\n");
        else printf("%d\n",ans[i]);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值