【[JXOI2017]加法】
阿新 • • 發佈:2019-01-01
江西竟然還有省選,而且還是可憐題,實在是有點可怕
這道題還是比較清真的,大概是最簡單的可憐題?
首先看到最大值最小,就很容易想到了二分答案
對於一個二分出來的答案\(mid\),去把原數列掃一遍就可以得到每一個位置至少要被覆蓋幾次
現在的問題變成了從\(m\)個區間裡選擇最少的區間,使得每一個位置都至少被覆蓋給定的次數
現在就變成一個貪心問題了
先把所有區間按照左端點排好序,之後開一根掃描線掃過去,掃的過程中開一個堆,用來儲存所有被掃描線掃到過左端點的區間
假如我們掃描線掃到一個位置\(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; }