Yoi #445. 寶藏
阿新 • • 發佈:2020-11-05
題面
藏寶圖上標出了\(n\)個深埋在地下的寶藏,第\(i\)個寶藏價值為\(w_i\),挖掘它需要時間\(t_i\)。
小明的總時間為T。
現在小明模擬\(Q\)次開採活動,第\(i\)次開採正好開採\(x_i\)個寶藏(保證\(x_i\)為奇數),他想要您計算開採的寶藏的價值的中位數最大是多少。
對於所有的資料,滿足\(n,Q\leq 3\times 10^5\ \ 0\leq w_i,t_i\leq 10^6\ \ 0\leq T\leq 10^{11} \ \ x_i\leq n\)。
分析
首先按\(w_i\)排序
考慮開採的中位數為\(w_i\)時能開採的最大寶藏數量具有二分性
所以二分+權值樹狀陣列維護
時間複雜度\(O(nlg^2 n)\)
不用考慮相同,因為設最終取出的答案為$ a_p1,a_p2,……,a_pk$,必定存在中位數
#include<bits/stdc++.h> #define ll long long using namespace std; inline int rd() { int ret=0; char ch=getchar(); while(ch<'0'||ch>'9') { ch=getchar(); } while(ch>='0'&&ch<='9') { ret=(ret<<1)+(ret<<3)+ch-'0'; ch=getchar(); } return ret; } const int N=3e5+5,M=1e6+5; int n,mx,ans[N]; ll m; struct A{ int t,w; }a[N]; bool cmp(A i,A j) { return i.w<j.w; } struct B{ int c[M],now; ll s[M],ret; inline void add(int x,int k) { for(int i=x;i<=mx;i+=i&-i) c[i]+=k,s[i]+=x*k; } inline ll sum(int x){ now=ret=0; if(x==0) return 0; for(int i=19;i>=0;i--) { if(now+(1<<i)<=mx&&c[now+(1<<i)]<x) { x-=c[now+(1<<i)]; ret+=s[now+(1<<i)]; now+=1<<i; } } return ret+(ll)(now+1)*x; } }c1,c2; int main() { freopen("treasure.in","r",stdin); freopen("treasure.out","w",stdout); int T; scanf("%d%lld%d",&n,&m,&T); for(int i=1;i<=n;i++) { scanf("%d%d",&a[i].w,&a[i].t); mx=max(mx,a[i].t+1); } sort(a+1,a+n+1,cmp); if(T==1) { int q=rd()>>1; for(int i=1;i<n;i++) { c1.add(a[i].t+1,1); } int l,r,mid,ret; for(int i=n;i>q;i--) { l=1,r=min(n-i,i-1),ret=0; if(a[i].t>m) { if(i!=1) c1.add(a[i-1].t+1,-1),c2.add(a[i].t+1,1); continue; } if(i<=n-q&&c1.sum(q)-q+c2.sum(q)-q+a[i].t<=m) { printf("%d\n",a[i].w); return 0; } if(i!=1) c1.add(a[i-1].t+1,-1),c2.add(a[i].t+1,1); } puts("-1"); return 0; } for(int i=1;i<n;i++) { c1.add(a[i].t+1,1); } int l,r,mid,ret; for(int i=n;i;i--) { l=1,r=min(n-i,i-1),ret=0; if(a[i].t>m) { if(i!=1) c1.add(a[i-1].t+1,-1),c2.add(a[i].t+1,1); continue; } while(l<=r) { mid=l+r>>1; if(c1.sum(mid)-mid+c2.sum(mid)-mid+a[i].t<=m) { ret=mid,l=mid+1; } else r=mid-1; } if(!ans[ret]) ans[ret]=a[i].w+1; if(i!=1) c1.add(a[i-1].t+1,-1),c2.add(a[i].t+1,1); } for(int i=n/2;i>=0;i--) { ans[i]=max(ans[i],ans[i+1]); } while(T--) { int q=rd()>>1; if(!ans[q]) puts("-1"); else printf("%d\n",ans[q]-1); } return 0; }