1. 程式人生 > >【[JXOI2017]加法】

【[JXOI2017]加法】

江西竟然還有省選,而且還是可憐題,實在是有點可怕

這道題還是比較清真的,大概是最簡單的可憐題?

首先看到最大值最小,就很容易想到了二分答案

對於一個二分出來的答案\(mid\),去把原數列掃一遍就可以得到每一個位置至少要被覆蓋幾次

現在的問題變成了從\(m\)個區間裡選擇最少的區間,使得每一個位置都至少被覆蓋給定的次數

現在就變成一個貪心問題了

先把所有區間按照左端點排好序,之後開一根掃描線掃過去,掃的過程中開一個堆,用來儲存所有被掃描線掃到過左端點的區間

假如我們掃描線掃到一個位置\(i\),且\(i\)之前的位置都已經被覆蓋了,那麼顯然這個時候我們需要從堆裡找到之前的那些能覆蓋到\(i\)

的區間來覆蓋掉\(i\),那麼自然是優先選擇那些右端點更靠右的區間,它們能覆蓋到更多的點

同時覆蓋的過程中需要一個區間加,單點查的資料結構,自然選擇樹狀陣列了

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<cmath>
#include<algorithm>
#define LL long long
#define lowbit(x) ((x)&(-x))
#define re register
#define maxn 100005
#define mp std::make_pair
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
int a[maxn],c[maxn];
struct Seg
{
    int l,r;
}b[maxn];
int n,m,K,D,T;
int pre[maxn],d[maxn];
inline int read()
{
    char c=getchar();
    int x=0;
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9')
        x=(x<<3)+(x<<1)+c-48,c=getchar();
    return x;
}
inline int cmp(Seg A,Seg B)
{
    return A.l<B.l;
}
inline void add(int x,int val)
{
    for(re int i=x;i<=n;i+=lowbit(i)) c[i]+=val;
}
inline int ask(int x)
{
    int now=0;
    for(re int i=x;i;i-=lowbit(i)) now+=c[i];
    return now;
}
typedef std::pair<int,int> pii;
std::priority_queue<pii> q;
inline int check(int x)
{
    memset(d,0,sizeof(d));
    for(re int i=1;i<=n;i++)
    {
        if(a[i]<x) d[i]=ceil(double(x-a[i])/double(D));
        if(d[i]>pre[i]) return 0;
    }
    memset(c,0,sizeof(c));
    while(!q.empty()) q.pop();
    int now=1,tot=0;
    for(re int i=1;i<=n;i++)
    {
        while(b[now].l==i) q.push(mp(b[now].r,b[now].l)),now++;
        if(!d[i]) continue;
        int cnt=ask(i);
        if(cnt>=d[i]) continue;
        while(!q.empty())
        {
            pii mid=q.top();
            q.pop();
            if(mid.first>=i) add(mid.second,1),add(mid.first+1,-1),cnt++,tot++;
            if(cnt==d[i]) break;
        }
        if(cnt<d[i]) return 0;
    }
    return tot<=K;
}
int main()
{
    T=read();
    while(T--)
    {
        n=read(),m=read(),K=read(),D=read();
        memset(pre,0,sizeof(pre)),memset(b,0,sizeof(b));
        for(re int i=1;i<=n;i++) a[i]=read();
        for(re int i=1;i<=m;i++) b[i].l=read(),b[i].r=read(),pre[b[i].l]++,pre[b[i].r+1]--;
        for(re int i=1;i<=n;i++) pre[i]+=pre[i-1];
        std::sort(b+1,b+m+1,cmp);
        int L=999999999,R;
        for(re int i=1;i<=n;i++)
            L=min(L,a[i]);
        R=L+D*K;
        int ans=0;
        while(L<=R)
        {
            int mid=L+R>>1;
            if(check(mid)) ans=mid,L=mid+1;
                else R=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}