1. 程式人生 > 實用技巧 >Yoi #445. 寶藏

Yoi #445. 寶藏

題面

藏寶圖上標出了\(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;
}