bzoj2527 [Poi2011]Meteors(整体二分+树状数组)

bzoj2527 [Poi2011]Meteors

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=2527

题意:
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。

数据范围
1<=n,m,k<=3*10^5
1<=Pi<=10^9
1<=Ai<10^9

题解:
就是说一个可以二分,一堆也可以二分。
感觉很像线段树分治,在某个区间,把左区间的操作都操作了,看看各个询问是否满足条件,满足条件说明答案在左区间,不然就丢到右区间,最后到单点就得到了答案在这个点的询问。

中间查询的时候1e9*1e9*1e9可能会爆longlong,要及时break掉。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#define LL long long
using namespace std;
const int N=300005;
const int inf=0x3f3f3f3f;
int n,m,p[N],k,L[N],R[N],A[N],id[N],ret[N],cur=0,tmp[N];
bool onleft[N];
struct BIT
{
    LL c[N]; 
    void insert(int x,LL d) {for(int i=x;i<=m;i=i+(i&(-i))) c[i]+=d;}
    LL query(int x) {LL ans=0; for(int i=x;i;i=i-(i&(-i))) ans+=c[i]; return ans;}
}Bit;
void Insert(int i,LL opt)
{
    if(L[i]<=R[i]) 
    {
        Bit.insert(L[i],opt*A[i]);
        Bit.insert(R[i]+1,-opt*A[i]);
    }
    else 
    {
        Bit.insert(L[i],opt*A[i]);
        Bit.insert(1,opt*A[i]);
        Bit.insert(R[i]+1,-opt*A[i]);
    }
}
vector<int> V[N];
void solve(int l,int r,int lf,int rg)
{
    if(l>r) return;
    if(lf==rg) {for(int i=l;i<=r;i++) ret[id[i]]=lf; return;}
    int mid=(lf+rg)>>1;
    while(cur<mid) {cur++; Insert(cur,1LL);}
    while(cur>mid) {Insert(cur,-1LL); cur--;}
    int top=0,cnt=0;
    for(int x=l;x<=r;x++)
    {
        int i=id[x]; int sz=V[i].size();
        LL ans=0;
        for(int j=0;j<sz;j++)
        {
            ans+=Bit.query(V[i][j]);
            if(ans>=p[i]) break;
        }
        if(ans>=p[i]) {onleft[i]=1; top++;}
        else onleft[i]=0;
    }
    for(int x=l;x<=r;x++)
    {
        int i=id[x];
        if(onleft[i]) tmp[++cnt]=i;
        else tmp[++top]=i;
    }
    for(int i=1;i<=top;i++) id[l+i-1]=tmp[i];
    solve(l,l+cnt-1,lf,mid); solve(l+cnt,r,mid+1,rg);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x;scanf("%d",&x);
        V[x].push_back(i);
    }
    for(int i=1;i<=n;i++) scanf("%d",&p[i]);
    scanf("%d",&k);
    for(int i=1;i<=k;i++) scanf("%d%d%d",&L[i],&R[i],&A[i]);
    k++; L[k]=1; R[k]=m; A[k]=inf;
    for(int i=1;i<=n;i++) id[i]=i;
    solve(1,n,1,k);
    for(int i=1;i<=n;i++)
    {
        if(ret[i]<k) printf("%d\n",ret[i]);
        else printf("NIE\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值