Gym - 101630G The Great Wall (二分+线段树) (2017–2018, NEERC – Northern Eurasia Finals)

 

解题思路:首先求第k小的答案,通常套路都是二分答案,然后判断有多少个答案比他小即可。

关键在于如何高效的判断有多少个答案比他小,如果我们把所有答案预处理出来,复杂度是N^2级别的,但是我们不必把所有答案都预处理出来,我们只需要知道有多少个比他小即可。因此我们可以通过各种数据结构,高效的查询有多少个比他小即可。这样复杂度就可以去到NLogN了。这里需要巧妙地转化,使得可以在O(NlogN)的时间内查询有多少个点比他小。附上官方题解!

最后我们可以用权值线段树或者树状数组即可快速查询,查询前把所有的b区间和 g都离散化即可。lower_bound注意-1的问题。

注意K要--,因为第1小其实是没有任何数比他小。

 

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=40005;
const int MAXM=MAXN*3;
ll a[MAXN];
ll b[MAXN];
ll c[MAXN];

ll suma[MAXN];
ll sumb[MAXN];
ll sumc[MAXN];

ll g[MAXN];
ll f[MAXN];

ll sorted[MAXN*5];
int tot=0;

int N,R;
ll K;
ll tree[MAXM<<2];

void update(int L,ll C,int l,int r,int rt){
    if(l==r){
        tree[rt]+=C;
        return;
    }
    int m=(l+r)/2;
    if(L<=m)
        update(L,C,l,m,rt<<1);
    else
        update(L,C,m+1,r,rt<<1|1);
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}

ll query(int L,int R,int l,int r,int rt){
    if(R<L)
        return 0;
    if(L<=l&&r<=R)
        return tree[rt];
    int m=(l+r)/2;
    ll ans=0;
    if(L<=m)
        ans+=query(L,R,l,m,rt<<1);
    if(R>m)
        ans+=query(L,R,m+1,r,rt<<1|1);
    return ans;
}

int id(ll num){
    return lower_bound(sorted,sorted+tot,num)-sorted+1;
}

bool judge(ll s){
    ll rank=0;
    memset(tree,0,sizeof(tree));
    for(int i=R+1,j=1;i+R-1<=N;i++,j++){
        update(id(sumb[j+R-1]-sumb[j-1]),1,1,MAXM,1);
        rank+=query(1,id(s-(sumb[i+R-1]-sumb[i-1]))-1,1,MAXM,1);
    }
    memset(tree,0,sizeof(tree));
    for(int i=2;i+R-1<=N;i++){
        update(id(g[i-1]),1,1,MAXM,1);
        if(i-R>=1)
            update(id(g[i-R]),-1,1,MAXM,1);
        rank+=query(1,id(s-f[i])-1,1,MAXM,1);
    }
    return rank<=K;
}


int main()
{
    scanf("%d%d%lld",&N,&R,&K);
    K--;//pay attention
    for(int i=1;i<=N;i++){
        scanf("%lld",&a[i]);
        suma[i]=suma[i-1]+a[i];
    }
    for(int i=1;i<=N;i++){
        scanf("%lld",&b[i]);
        sumb[i]=sumb[i-1]+(b[i]-a[i]);
    }
    for(int i=1;i<=N;i++){
        scanf("%lld",&c[i]);
        sumc[i]=sumc[i-1]+(c[i]-a[i]);
    }

    for(int i=1;i+R-1<=N;i++){
        g[i]=(sumc[i-1]-2*sumb[i-1])+(sumc[i+R-1]-sumb[i+R-1])-(sumc[i-1]-sumb[i-1]);
        f[i]=(2*sumb[i-1]-sumc[i-1])+(sumb[i+R-1])-(sumb[i-1]);
    }

    //li san hua
    for(int i=1;i<=N;i++){
        if(i>=R){
            sorted[tot++]=g[i-R+1];
            sorted[tot++]=sumb[i]-sumb[i-R];
        }
    }
    sort(sorted,sorted+tot);
    tot=unique(sorted,sorted+tot)-sorted;

    ll l=0,r=1000000000000ll;
    while(l+1<r){
        ll m=(l+r)/2;
        if(judge(m))
            l=m;
        else
            r=m;
    }

    printf("%lld\n",l+suma[N]);
    return 0;
}










 

发布了402 篇原创文章 · 获赞 67 · 访问量 16万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 点我我会动 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览