整体二分——BZOJ2527/Luogu3527 [POI2011]MET-Meteors

题面:洛谷3527 BZOJ2527
这题首先可以想到二分答案但是呢有很多点需要二分
同时每个点的二分状态都差不多
那么我们就一起二分(整体二分)
每次二分出答案以后对于每个国家计算有几颗流星
这个树状数组和线段树都可以维护(我用树状数组调了一个上午QAQ)
然后超过希望总数的点放到左边去,没有的放到右边去
然后分别继续二分
对然后统计答案即可

#include<bits/stdc++.h>
using namespace std;
typedef long long lll;
int n,m,k,nedge=0,p[300001],nex[300001],head[300001];
int v[300001],L[300001],R[300001],t[300001],ans[300001];
lll p1[300001],p2[300001],rp=0,f[300001];
bool jzq[300001];
inline int lowbit(int x){return(x&-x);}
inline void add(int x,lll s){for(;x<=m;x+=lowbit(x))f[x]+=s;}
inline lll sum(int x){lll ans=0;for(;x>0;x-=lowbit(x))ans+=f[x];return ans;}
inline void addedge(int a,int b){p[++nedge]=b;nex[nedge]=head[a];head[a]=nedge;}
void jia(int x,int v){
    int l=L[x],r=R[x],vv=v*t[x];
    if(r>=l)add(r+1,-vv),add(l,vv);
    else add(1,vv),add(r+1,-vv),add(l,vv);
}
void erfen(int l,int r,int ll,int rr){
    if(rr<ll)return;
    if(l==r){
        for(int i=ll;i<=rr;i++)ans[p1[i]]=l;
        return;
    }
    int mid=l+r>>1;
    while(rp<mid)jia(++rp,1);while(rp>mid)jia(rp--,-1);
    lll s;
    for(int i=ll;i<=rr;i++){
        p2[i]=p1[i];s=0;
        for(int k=head[p1[i]];k;k=nex[k]){
            s+=sum(p[k]);
            if(s>=v[p1[i]])break;
        }
        jzq[p1[i]]=(s>=v[p1[i]])?1:0;
    }
    s=ll-1;int ss=0;
    for(int i=ll;i<=rr;i++)if(jzq[p2[i]])p1[++s]=p2[i];
    for(int i=ll;i<=rr;i++)if(!jzq[p2[i]])p1[++ss+s]=p2[i];
    erfen(l,mid,ll,s);
    erfen(mid+1,r,s+1,rr);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int x;scanf("%d",&x);
        addedge(x,i);
    }
    for(int i=1;i<=n;i++)scanf("%d",&v[i]);
    scanf("%d",&k);
    for(int i=1;i<=k;i++)scanf("%d%d%d",&L[i],&R[i],&t[i]);
    L[++k]=1;R[k]=m;t[k]=1e9;
    for(int i=1;i<=n;i++)p1[i]=i;
    erfen(1,k,1,n);
    for(int i=1;i<=n;i++)if(ans[i]!=k)printf("%d\n",ans[i]);
    else puts("NIE");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值