此题不提供链接
题目描述
题解
这题真的让我降智了。
一开始打了个 O ( n log 2 n ) O(n\log ^2n) O(nlog2n) 的做法,想着回来再用zkw卡常就去看下一题了,结果最后没时间回来卡,加上大样例太水,MLE+WA+TLE。
首先可以看出,如果在开采量为 x x x 时中位数可以为 w i w_i wi,那么开采量 < x <x <x 时仍然可以。所以我们可以对每个数,求出这个数可做中位数的最大开采量,然后对贡献做一个后缀取 max \max max 即可。
怎么求呢?初步想法就是把原数组按 w w w 排序,然后二分,判断取前面 t t t 最小的 x x x 个和后面 t t t 最小的 x x x 个是否合法。这个做法是 n log 2 n n\log^2n nlog2n 的,后面的判断可以用线段树二分或平衡树。
但是实际上两个 log \log log 过不去,除非二分剪枝优化加上zkw线段树还要疯狂卡一堆常。
我们可以按 w w w 从大到小倒着枚举每个数,那么这个数的合法最大开采量只有大于前面最大的合法开采量才有意义,否则一定不优。
所以我们不再需要二分了,因为最大开采量递增,直接枚举即可做到 O ( n log n ) O(n\log n) O(nlogn)。
代码
最快rank1代码,zkw线段树二分
(hs那个普通线段树4664ms的真的佩服,不知道怎么卡常的,可惜已经被我超过了)
#include<cstdio>//JZM yyds!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<random>
#define ll long long
#define uns unsigned
#define MOD
#define MAXN 300005
#define INF 1e17
#define IF (it->first)
#define IS (it->second)
using namespace std;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0)f^=(s=='-'),s=getchar();
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
return f?x:-x;
}
int pt[30],lp;
inline void print(ll x){
if(x<0)putchar('-'),x=-x;
pt[lp=1]=x%10;
while(x>9)x/=10,pt[++lp]=x%10;
while(lp)putchar(pt[lp--]^48);
}
inline ll lowbit(ll x){return x&-x;}
int n,Q;
ll T,ans[MAXN];
struct itn{
ll w,t;itn(){}
itn(ll W,ll T){w=W,t=T;}
bool operator<(const itn&b)const{
if(w^b.w)return w<b.w;
else return t<b.t;
}
}a[MAXN];
int p;
struct zkw{
ll f[3000005];
int g[3000005];
inline void add(int x,ll df,int dg){
for(x=p+x;x;x>>=1)f[x]+=df,g[x]+=dg;
}
inline ll sch(int k){
if(g[1]<=k)return f[1];
if(k<=0)return 0;
int x=1;ll res=0;
while(x<p){
if(g[x<<1]>=k)x<<=1;
else res+=f[x<<1],k-=g[x<<1],x=x<<1|1;
}
return res+(x-p)*k;
}
}A,B;
inline ll check(int m){
if(A.g[1]<m||B.g[1]<m)return T+1;
return A.sch(m)+B.sch(m);
}
signed main()
{
freopen("treasure.in","r",stdin);
freopen("treasure.out","w",stdout);
n=read(),T=read(),Q=read();
memset(ans,-1,sizeof(ans));
for(int i=1;i<=n;i++)a[i].w=read(),a[i].t=read();
sort(a+1,a+1+n),p=1<<20;
for(int i=1;i<n;i++)A.add(a[i].t,a[i].t,1);
for(int i=n,j=0;i>0;i--){
if(a[i].t<=T){
int re=1;
while(check(j)<=T-a[i].t)re=++j;
ans[re]=max(ans[re],a[i].w);
j=max(j,re);
}
B.add(a[i].t,a[i].t,1);
if(i>1)A.add(a[i-1].t,-a[i-1].t,-1);
}
for(int i=n;i>0;i--)ans[i]=max(ans[i],ans[i+1]);
while(Q--)print(ans[(read()+1)>>1]),putchar('\n');
return 0;
}