题目:WZT 有很多妹子。妹子们站成一排,每个妹子都有一个美貌值。虽然 WZT 的妹子太多了,但是妹子的“水平”肯定 是不会差的啦。现在 WZT 要买很多衣服给妹子穿,来改变妹子的美貌值。但是 WZT 比较忙,又要给上百万个妹子买衣 服,于是只会买同一款式的衣服。 WZT 已经准备好了 种购物方案,第 种方案是给第 个到第 个妹子各买一件衣服,使其美貌值均增加 ,但 是他只打算使用其中 种购买方案。 WZT 当然是会雨露均沾的啦!所以他希望让美貌值最小的妹子的美貌值最大
输入格式
第一行输入一个整数
t
t
t ,表示数据组数。 对于每组数据第一行输入四个正整数
n
m
k
a
n\ m\ k\ a
n m k a,分别表示 WZT 妹子的数量,买衣服的方案数,WZT 打算采用的方案数,衣服的美貌值。保证
k
≤
m
k\le m
k≤m。 第二行输入
n
n
n个正整数,其中第
i
i
i个数表示第
i
i
i个妹子的美貌值。 接下来 行每行两个整数
l
,
r
l,r
l,r ,描述一种方案。保证所有方案两两不同,且
1
≤
l
i
≤
r
i
≤
n
1\le li\le ri\le n
1≤li≤ri≤n 。
输出格式
对于每组数据输出一个整数表示买完衣服后妹子美貌值的最小值的最大值。
solution:
首先我们看到题目要求最小值最大,我们马上就会想到利用二分将其转化成为判定性问题。
那么我们怎么判断一个答案是否可行呢?
显而易见,如果有一个妹子的美貌值比答案低,那么我们就必须给她穿衣服,直到她的美貌值大于或等于当前二分的答 案。
那么当给一个妹子穿衣服时,有多种方案,但我们不必全选,又该怎么选择呢?
我们发现,我们可以从左往右给妹子穿衣服,这样每次我们选择包含该点的方案中,选择还没选过的右端点最右的一个 方案,就一定是最优的。因为,当我们给某个妹子穿衣服时,她左边的妹子一定穿好衣服了(因为我们是从左往右穿衣 服的),那么对于一种方案,只要包含这个妹子,那么它左端点在哪里都不重要;而我们显然希望给这个妹子穿衣服的 时候,给那些还没穿好衣服的妹子顺便穿上衣服,这样肯定不会更劣。
那么怎么选择符合条件的方案呢?
首先,一个方案得包含这个妹子,所以我们可以在从左往右的过程中,把所有左端点从这个妹子开始的方案加到待选方 案集合中,每次穿衣服时选择集合中右端点最右的方案(假如右端点最右的方案不包含这个妹子,显然没有没被选择的 包含这个妹子的方案,就挂掉了),并把这个方案从集合里删去。
考虑维护这个集合,你当然可以使用平衡树(码量巨大)和
s
t
d
:
:
s
e
t
std::set
std::set(常数巨大),但观察我们需要的操作,我们只 要用一个堆来维护就好了。
还有一个问题选择一个方案以后,怎么实现给妹子穿衣服的操作呢?
你当然可以使用线段树或树状数组(也可以通过本题,不过比较麻烦&&常数大),但我们有一个更好的做法。
我们知道,区间加法可以通过对差分数组的修改来实现,但每次查询需要从前面往后面加一遍。
但在这题中,我们不需要往一个点的左边去加,只要在这个点当前值上面加就好了,因为往左加明显对答案的判定没有 影响,那么我们只要对这个点和右端点的差分数组进行修改就可以实现这个操作。
所以我们从左到右做的过程中,把差分值加起来得到当前值,用一个堆实现查询方案,区间加的时候只要修改当前值和 右端点的差分值(准确来说是右端点再右边一个的差分值,但这个是差分的基本操作,说右端点比较简单明了,我就这 么讲了),就实现了对问题的判定,由于使用了堆,这样的一次判定是
O
(
n
l
o
g
m
)
O(nlogm)
O(nlogm) 的。
外面套了一个二分,所以总共的时间复杂度是
O
(
n
l
o
g
m
l
o
g
(
O(nlogmlog(
O(nlogmlog(最大美貌值之差
)
)
))
)) 的,这样你就通过了本题。
上面是题解的话我当然不带了打这么多了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define N 200005
using namespace std;
int t,n,m,k,up,a[N],mx,mn,b[N],sum[N];
priority_queue<int> q;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
struct qwq{
int l,r;
bool operator <(const qwq &x) const{
return l<x.l||(l==x.l&&r<x.r);
}
}quq[N];
inline bool check(int x){
int now=1,cnt=0; memset(sum,0,sizeof sum);
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++){
if(a[i]<x) b[i]=x-a[i];
else b[i]=0;
sum[i]+=sum[i-1]; b[i]-=sum[i];
for(;now<=m && quq[now].l<=i;now++){
q.push(quq[now].r);
}
while(b[i]>0 && !q.empty()){
int x=q.top(); cnt++; q.pop();
if(x>=i) b[i]-=up,sum[i]+=up,sum[x+1]-=up;
}
if(cnt>k || b[i]>0) return false;
}
return cnt<=k;
}
int main(){
freopen("frineds.in","r",stdin);
freopen("frineds.out","w",stdout);
t=rd();
while(t--){
n=rd(); m=rd(); k=rd(); up=rd(); mn=1e9; mx=0;
for(int i=1;i<=n;i++) a[i]=rd(),mx=max(mx,a[i]),mn=min(mn,a[i]);
mx+=up*k;
for(int i=1;i<=m;i++){
quq[i].l=rd(),quq[i].r=rd();
}
sort(quq+1,quq+m+1);
int l=mn,r=mx,mid,ans=0;
while(l<=r){
mid=(l+r)>>1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}