【NOIP2017提高A组集训10.28】序列操作

Description

一开始有n个非负整数hi,接下来会进行m次操作,第i次操作给出一个数c[i],要求你选出c[i]个大于零的数并将它们减去1。
问最多可以进行多少轮操作后无法操作(即没有c[i]个大于零的数)

Solution

这题数据范围出的很迷,log^2竟然都能过
很显然我们只用给前k大的数减一,然后我们考虑一段数减完之后相对顺序会怎么变,我们可以发现只有序列末尾相等的那一段会移到那一段的前面,所以在相等的那一段我们可以考虑在相等的那一段只减前面的数。
那么我们可以用线段树来维护,或者打个树状数组加二分又短又快。

Code

#include
#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=1e6+7;
int a[maxn];
int i,j,k,l,n,m,ans,x,y;
bool bz;
int t[maxn],u,v;
int get(){
    char ch=getchar();int x=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x;
}
bool cmp(int x,int y){return x>y;}
void add(int x,int y){
    for(;x<=n+1;x+=(x&-x))t[x]+=y;
}
int find(int x){
    int z=0;
    for(;x;x-=(x&-x))z+=t[x];
    return z;
}
int zhao(int x){
    int l=1,r=n+1,mid;
    while(l2;
        if(mid==n+1){
            ans=ans;
        }
        if(find(mid)<=x)r=mid;else l=mid+1;
    }
    return l;
}
int main(){
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n)a[i]=get();sort(a+1,a+1+n,cmp);
    fo(i,1,n)add(i,a[i]),add(i+1,-a[i]);
    fo(i,1,m){
        v=get();
        u=find(v);
        if(!u)break;
        x=zhao(u);y=zhao(u-1);
        add(1,-1);add(x,1);
        if(y-(v-(x-1))>0)add(y-(v-(x-1)),-1),add(y,1);
        ans=i;
    }
    printf("%d\n",ans);
}

你可能感兴趣的:(noip,线段树,二分,暴搜,树状数组)