【bzoj2527】Meteors【整体二分】

n 个国家和m个空间站,每个空间站都属于一个国家,一个国家可以有多个空间站,所有空间站按照顺序形成一个环,也就是说, m 号空间站和1号空间站相邻。
现在,将会有 k 场流星雨降临,每一场流星雨都会给区间[li,ri]内的每个空间站带来 ai 单位的陨石,每个国家都有一个收集陨石的目标 pi ,即第 i 个国家需要收集pi单位的陨石。
询问:每个国家最早完成陨石收集目标是在第几场流星雨过后。
数据范围: 1n,m,k300000

不知道为啥拐进来分治这个坑了- -
整体二分非常神奇,它把所有询问放在一起处理(前提是修改操作互不影响),然后一次出解。
具体说来用一个void solve(int head,int tail,int l,int r),处理head到tail的询问,保证这些询问的答案在l到r之间。
然后扫描一遍head到tail,若修改操作修改的值不超过mid则修改(对于这题来说只要执行第l到第mid个修改即可),然后将询问分成左右两半分别递归。
更详细的请看这里
Meteors:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxm=300001,maxn=maxm,maxk=maxn;
int n,m,k;
struct BIT{
    ll c[maxm];
    void add(int x,ll d){for(;x<=m;x+=x&-x)c[x]+=d;}
    ll at(int x){ll ans=0;for(;x;x^=x&-x)ans+=c[x];return ans;}
    void add(int l,int r,ll d){
        if(l<=r){add(l,d);add(r+1,-d);}
        else{add(l,d);add(1,d);add(r+1,-d);}
    }
}t;
int rain[maxk][3];
int id[maxn],p[maxn],cur[maxn],tmp[maxn],tol[maxn],tor[maxn];
int tot,ans[maxn],list[maxn],next[maxn],pos[maxm];
inline void add(int b,int c){
    tot++;
    pos[tot]=b;
    next[tot]=list[c];
    list[c]=tot;
}
void solve(int head,int tail,int l,int r){
    if(head>tail) return;
    int i,j,x,mid=(l+r)>>1,lnum=0,rnum=0;
    if(l==r){
        for(i=head;i<=tail;++i) ans[id[i]]=l;
        return;
    }
    for(i=l;i<=mid;++i) t.add(rain[i][0],rain[i][1],rain[i][2]);
    for(i=head;i<=tail;++i){
        tmp[x=id[i]]=0;
        for(j=list[x];j;j=next[j]){
            tmp[x]+=t.at(pos[j]);
            if(tmp[x]+cur[x]>p[x])break;
        }
        if(tmp[x]+cur[x]>=p[x]) tol[++lnum]=x;
        else cur[x]+=tmp[x],tor[++rnum]=x;
    }
    for(i=l;i<=mid;++i) t.add(rain[i][0],rain[i][1],-rain[i][2]);
    memcpy(id+head,tol+1,lnum<<2);//一个int占4个字节
    memcpy(id+head+lnum,tor+1,rnum<<2);
    solve(head,head+lnum-1,l,mid);
    solve(head+lnum,tail,mid+1,r);
}
inline int read(){
    int x=0;bool f=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=1;ch=getchar();}
    while(isdigit(ch)) x=x*10+ch-48,ch=getchar();
    return f?-x:x;
}
int main(){
    n=read();m=read();
    int i,j;
    for(i=1;i<=m;++i)add(i,read());
    for(i=1;i<=n;++i)p[i]=read();
    k=read();
    for(i=1;i<=k;++i)rain[i][0]=read(),rain[i][1]=read(),rain[i][2]=read();
    rain[++k][0]=1,rain[k][1]=m,rain[k][2]=~0U>>1;
    for(i=1;i<=n;++i)id[i]=i;
    solve(1,n,1,k);
    for(i=1;i<=n;++i)
        if(ans[i]!=k)printf("%d\n",ans[i]);
        else puts("NIE");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值