题解 P3527 【[POI2011]MET-Meteors】

· · 题解

这道题整体二分,这个整体二分很神奇,类似于二分答案

首先,我们要二分每一个国家所需要的次数,同时用树状数组维护区间的陨石数量

如果是分别枚举每一个国家所需的时间,因为要累加,总复杂度是O(nmlgmlgk),会TLE

这时候,便要用整体二分——我们将所有的国家的时间一起二分:

每一次,取一个mid的时间,并计算出区间内每个国家收集的数量,把所有能够完成国家的放在一边,把所有不能的国家放在另一边

然后把左边和右边分为作为一个区间

左区间继续二分mid以下的时间,右区间继续二分mid以上的时间

这样的复杂度就变成了O(nlgnlgk),同时也能求出每一个国家所需的时间

不过有些地方要注意优化,不然会卡常

累计的话树状数组直接暴力算就行了

下面是代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
using namespace std;
#define MAXN 300010
#define LLI long long
int n,m,belong[MAXN],ls[MAXN],rs[MAXN],target[MAXN],k,
    val[MAXN],now,cont[MAXN],lt[MAXN],rt[MAXN],ans[MAXN];
LLI tree[MAXN],tc[MAXN];
vector<int>T[MAXN];//每个国家所拥有的空间站 
void add(int x,int v){
    for(;x<=m;x+=x&-x)
    tree[x]+=v;
}//树状数组修改 
LLI query(int x){
    LLI res=0;
    for(;x;x-=x&-x)
        res+=tree[x];
    return res;
}//树状数组查询 
void change(int l,int r,int x){//树状数组区间加
    if(l<=r){
        add(l,x);add(r+1,-x);
    }else{
        add(l,x);add(m+1,-x);
        add(1,x);add(r+1,-x);
        //这里考虑到是环形,所以分为两段区间
    }
}
int t;
void read(int&x){x=0;while(t=getchar(),!isdigit(t));while(x=(x<<1)+(x<<3)+t-48,t=getchar(),isdigit(t));}
//快读
void query(int l,int r,int L,int R){
    if(l>r)return;if(L>R)return;//这里貌似有一句可以不用写 
    if(l==r){
        for(register int i=L;i<=R;++i)
            ans[cont[i]]=l;//如果某个(些)国家二分到了答案,就记录下来
            //cont是国家的编号 
        return;
    }
    int mid=l+r>>1;
    while(now<mid)++now,change(ls[now],rs[now],val[now]); 
    while(now>mid)change(ls[now],rs[now],-val[now]),--now;
    //改变当前树状数组的状态(把状态从某个时间移到另一个时间) 
    for(int i=L;i<=R;++i){
        tc[cont[i]]=0;
        for(int j=0;j<T[cont[i]].size();++j)
            tc[cont[i]]+=query(T[cont[i]][j]);//累加每个国家的数量 
    }
    int lct=0,rct=0;
    for(register int i=L;i<=R;++i)
        if(tc[cont[i]]>=target[cont[i]])lt[++lct]=cont[i];//把可以的国家归于左区间 
        else rt[++rct]=cont[i];//把不可以的国家归于右区间 
    int mid2=lct;
    for(register int i=1;i<=lct;++i)cont[i+L-1]=lt[i];
    for(register int i=1;i<=rct;++i)cont[L+lct+i-1]=rt[i];//复制区间 
    query(l,mid,L,L+mid2-1);
    query(mid+1,r,L+mid2,R);//继续二分 
}
int main(){
    read(n),read(m);
    for(register int i=1;i<=m;++i){
        read(belong[i]);
        T[belong[i]].push_back(i);
    }
    for(register int i=1;i<=n;++i){
        read(target[i]);
        cont[i]=i;//国家列表初始化 
    }
    read(k);
    for(register int i=1;i<=k;++i){
        read(ls[i]);
        read(rs[i]);
        read(val[i]);
    }
    ++k;ls[k]=1;rs[k]=m;val[k]=0x3f3f3f3f;//在第k+1的时刻设置无限个陨石,即无解的条件
    now=0; 
    query(1,k,1,n);
    for(register int i=1;i<=n;++i)
        if(ans[i]==k)puts("NIE"); 
        else printf("%d\n",ans[i]);
    return 0;
}