hdu3486 暴力+線段樹

題意:選出最小的段數 使得段中的最大值的和要超過k

解法:線段樹寫這題確實不怎麼合適 不管怎麼說還是rmq的查詢效率比較高的 

即使這樣 也要加上優化 就是判斷二分時的上下界的極限 還有預判是否k永遠無法到達和是否最小值就已經超過k了

大概極限數據比較多的 所以可過很快過 

#include<cstdio>
#include<algorithm>
using namespace std;
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((l+r)>>1)

#define maxn 222222
int ma[maxn<<2],a[maxn],m,n,k;
void up(int rt){
    ma[rt]=max(ma[ls],ma[rs]);
}
void build(int rt,int l,int r){
    if(l==r){
        ma[rt]=a[l];
        return;
    }
    build(ls,l,mid);
    build(rs,mid+1,r);
    up(rt);
}
int query(int rt,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        return ma[rt];
    }
    int ans=0;
    if(L<=mid)ans=max(ans,query(ls,l,mid,L,R));
    if(mid<R)ans=max(ans,query(rs,mid+1,r,L,R));
    return ans;
}
int scan()
{
    int res=0,ch;
    while(!((ch= getchar())>='0'&&ch<='9')){
        if(ch==EOF)return 1<<30;
    }
    res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+(ch-'0');
    return res;
}
int judge(int size){
    int d=n/size,now,li=d*size,ans=0;
    for(int i=1;i<=li;i+=d){
        now=query(1,1,n,i,i+d-1);
        ans+=now;
    }
    return ans>k;
}
int main(){
    while(~scanf("%d%d",&n,&k)){
        if(n==-1&&k==-1)break;
        int tot=0,ma=0,mi=0;
        for(int i=1;i<=n;++i){
            a[i]=scan();tot+=a[i];ma=max(a[i],ma);
            mi=min(mi,a[i]);
        }
        
        if(tot<k){
            printf("-1\n");continue;
        }
        if(ma>k){
            printf("1\n");continue;
        }
        build(1,1,n);
        ma=(ma==0)?1:ma;
        mi=(mi==0)?1:mi;
        int l=1,r=n,ans=-1;
        r=min((k/mi+1),n),l=k/ma;
        if(l==0)l++;
        while(l<=r){
            if(judge(mid)){
                ans=mid;
                r=mid-1;
            }else l=mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章