首页 > 其他 > 详细

牛客网 中南林业科技大学第十一届程序设计大赛J题 二分+线段树

时间:2018-05-24 22:13:24      阅读:188      评论:0      收藏:0      [点我收藏+]

https://www.nowcoder.com/acm/contest/124#question

题意  找第一个不小于K的数的下标,然后对它前一个数加一

解析   我们可以维护一个最大值数组  1到 i的 最大值 就是max[ i ]  二分找到最左边的值 但是 找到的前一个加1 要用线段树来维护最大值

但是 这么写会超时。。。

超时代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007,maxn=1e6+50;
int sum[maxn<<2];
int a[maxn],n,m;
void PushUP(int rt)
{
    sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
}
void Build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=a[l];
        return;

    }
    int m=(l+r)>>1;
    Build(l,m,rt<<1);
    Build(m+1,r,rt<<1|1);
    PushUP(rt);
}
void Update(int L,int C,int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]+=C;
        return;
    }
    int m=(l+r)>>1;
    if(L<=m)
        Update(L,C,l,m,rt<<1);
    else
        Update(L,C,m+1,r,rt<<1|1);
    PushUP(rt);
}
int Query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        return sum[rt];
    }
    int m=(l+r)>>1;
    int ans=-100000000;
    if(L<=m)
        ans=max(ans,Query(L,R,l,m,rt<<1));
    if(R>m)
        ans=max(ans,Query(L,R,m+1,r,rt<<1|1));
    return ans;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(sum,0,sizeof(sum));
        memset(a,0,sizeof(a));
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
        }
        Build(1,n,1);
        int l,r,k;
        while(m--)
        {
            l=1,r=n;
            scanf("%d",&k);
            //cout<<Query(1,n,1,n,1)<<endl;
            if(Query(1,n,1,n,1)<k)
            {
                printf("are you ok\n");
                continue;
            }
            while(l<=r)
            {
                int mid=(l+r)>>1;
               // cout<<mid<<" "<<Query(1,mid,1,n,1)<<endl;
                if(Query(1,mid,1,n,1)>=k)
                    r=mid-1;
                else
                    l=mid+1;
            }
            printf("%d\n",l-1);
            if(l-1)
                Update(l-1,1,1,n,1);
        }
    }
}

 

q的 询问比较多应该是卡了常数  我们要优化一下  因为 线段树查询的时候就是二分  区间最大值是递增的 我们直接利用这个特点来操作

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007,maxn=1e6+50;
int sum[maxn<<2];
int a[maxn],n,m;
void PushUP(int rt)
{
    sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
}
void Build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=a[l];
        return;

    }
    int m=(l+r)>>1;
    Build(l,m,rt<<1);
    Build(m+1,r,rt<<1|1);
    PushUP(rt);
}
void Update(int L,int C,int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]+=C;
        return;
    }
    int m=(l+r)>>1;
    if(L<=m)
        Update(L,C,l,m,rt<<1);
    else
        Update(L,C,m+1,r,rt<<1|1);
    PushUP(rt);
}
int query(int L,int R,int l,int r,int rt,int p)
{
    if(l==r)
    {
        return l;
    }
    int m=(l+r)>>1;
    if(sum[rt<<1]>=p)                    //二分查询
        return query(L,R,l,m,rt<<1,p);
    return query(L,R,m+1,r,rt<<1|1,p);
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(sum,0,sizeof(sum));
        memset(a,0,sizeof(a));
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
        }
        Build(1,n,1);
        int l,r,k;
        while(m--)
        {
            l=1,r=n;
            scanf("%d",&k);
            if(sum[1]<k)
            {
                printf("are you ok\n");
                continue;
            }
            int ans=query(1,n,1,n,1,k);
            printf("%d\n",ans-1);
            if(ans-1)
                Update(ans-1,1,1,n,1);
        }
    }
}

其实 还有更简单的做法 因为 修改的是前一个值 而且找的是满足条件中最左边的 所以前一个+1 并不会 影响数组的单调性 变得只有前面一个的最大值  更新一下就好了

#include<bits/stdc++.h>
using namespace std;
int a[1000005],b[1000005];
int n,q,k;
int main()
{
    while(~scanf("%d%d",&n,&q))
    {
        int mx=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            b[i]=mx=max(mx,a[i]);
        }
        while(q--)
        {
            scanf("%d",&k);
            int l=lower_bound(b,b+n,k)-b;
            if(l==n){
                printf("are you ok\n");
                continue;
            }
            printf("%d\n",l);
            if(l==0)continue;
            a[l-1]++;
            b[l-1]=max(a[l-1],b[l-1]);
        }
    }
}

 

牛客网 中南林业科技大学第十一届程序设计大赛J题 二分+线段树

原文:https://www.cnblogs.com/stranger-/p/9085367.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!