1. 程式人生 > >Gym - 101630G The Great Wall (二分+線段樹) (2017–2018, NEERC – Northern Eurasia Finals)

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;
}