题解 大鱼吃小鱼

传送门

先放结论:\(O(nlog^3n)\) 可以过,于是下面可以跳过了(弥天大雾

先考虑暴力,显然是贪心不断选最大的
发现如果我们令第一个大于当前体积的鱼的体积为 \(B\)
则直到当前体积 \(>B\) 时候选集合才会发生变化
一个简单的思路是将能吃的鱼体积排序后二分
因为每这样做一次一定吃了一条体积大于等于自己的鱼
所以体积至少翻倍,只会做log次
但还有插入和删除的操作,无法方便地得到有序数组,且每个元素只能产生一次贡献不好实现

  • 当需要动态维护一个有序数组时:线段树平衡树均可,但不把问题抽象成这样一个裸的问题不好想到
  • 当需要动态维护一个有序数组,还要能在上面做二分时:可以线段树上二分
  • 如果想在一个有序序列上二分(二分值或区间和均可),同时还要支持动态修改,考虑线段树上二分
  • 关于在线段树上任意位置/区间二分:
    可以发现,若是要求在线段树上直接二分出一个(比如说)前缀和小于某个数的位置是容易的
    但若是指定了区间 \([x, y]\) 内的前缀和小于某个数的位置貌似不好操作
    其实你也可以直接算出 \([1, x]\) 的区间和,然后转化成上面的问题
    但是有一个更加巧妙的做法:
    区间 \([x, y]\) 在线段树上是log个区间,所以可以方便地将它们一个log取出来
    然后按顺序减掉这些区间的区间和,这样就可以找到最终答案落在哪个区间内
    于是在这样一个区间内二分就很容易了
    实现的话可以开个栈存节点
  • 关于在线段树上临时删除一个区间:
    可以打标记或将要删的区间扔到栈里实现,重点在于复原
    如果打标记的话删标记同理,复杂度 \(O(logn)\)
    但是将要删的区间扔到栈里的话复原是 \(O(log^2n)\) 的(每个区间都要pushup一次)

回到这个题,我们每次找最大的一些鱼,将自己吃到候选集合再次发生变化
然后利用区间临时删除删掉这些鱼,重复上述过程,直到达到要求的大小位置
复杂度 \(O(nlog^2n)\)

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 500010
#define ll long long
#define fir first
#define sec second
#define make make_pair
// #define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline ll read() {
	ll ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m, q;

namespace force{
	int top;
	ll sta[N];
	priority_queue<ll> q2;
	int calc(ll s, ll k) {
		if (s>=k) return 0;
		while (q2.size()) q2.pop();
		int pos1=1, pos2=lower_bound(sta+1, sta+top+1, s)-sta, cnt=0;
		while (pos1<pos2) q2.push(sta[pos1++]);
		while (q2.size()) {
			s+=q2.top(); q2.pop(); ++cnt;
			if (s>=k) return cnt;
			pos2=lower_bound(sta+1, sta+top+1, s)-sta;
			while (pos1<pos2) q2.push(sta[pos1++]);
		}
		assert(s<k);
		return -1;
	}
	void solve() {
		for (int i=1; i<=n; ++i) sta[++top]=read();
		sort(sta+1, sta+n+1);
		q=read();
		ll s, k, w;
		for (int i=1,op; i<=q; ++i) {
			op=read();
			if (op==1) {
				s=read(); k=read();
				printf("%d\n", calc(s, k));
			}
			else if (op==2) {
				sta[++top]=read();
				sort(sta+1, sta+top+1);
			}
			else {
				w=read();
				swap(*lower_bound(sta+1, sta+top+1, w), sta[top]);
				--top;
				sort(sta+1, sta+top+1);
			}
		}
	}
}

namespace task{
	int usize, cnt2[N], sta[N], rsta[N], top, rtop, ctop;
	int tl[N<<2], tr[N<<2], del[N<<2], cnt[N<<2], cntb[N<<2];
	ll dat[N<<2], val[N<<2], datb[N<<2], valb[N<<2], uni[N], w[N];
	pair<int, int> rec[N];
	struct que{int op; ll s, k, w;}q[N];
	struct tpl{
		ll fir, sec, thr;
		tpl(){}
		tpl(ll a, ll b, ll c){fir=a; sec=b; thr=c;}
		inline void build(ll a, ll b, ll c){fir=a; sec=b; thr=c;}
	};
	#define tl(p) tl[p]
	#define tr(p) tr[p]
	#define dat(p) dat[p]
	#define del(p) del[p]
	#define cnt(p) cnt[p]
	#define datb(p) datb[p]
	#define cntb(p) cntb[p]
	#define val(p) val[p]
	#define pushup(p) dat(p)=dat(p<<1)+dat(p<<1|1), cnt(p)=cnt(p<<1)+cnt(p<<1|1)
	void build(int p, int l, int r) {
		tl(p)=l; tr(p)=r;
		if (l==r) {val(p)=uni[l]; cnt(p)=cnt2[l]; dat(p)=val(p)*cnt(p); return ;}
		int mid=(l+r)>>1;
		build(p<<1, l, mid);
		build(p<<1|1, mid+1, r);
		pushup(p);
	}
	void upd(int p, int pos, int dlt) {
		if (tl(p)==tr(p)) {cnt(p)+=dlt; dat(p)=val(p)*cnt(p); return ;}
		int mid=(tl(p)+tr(p))>>1;
		if (pos<=mid) upd(p<<1, pos, dlt);
		else upd(p<<1|1, pos, dlt);
		pushup(p);
	}
	void lsplit(int p, int l, int r) {
		if (!dat(p)) return ;
		if (l<=tl(p) && r>=tr(p)) {sta[++top]=p; return ;}
		int mid=(tl(p)+tr(p))>>1;
		if (l<=mid) lsplit(p<<1, l, r);
		if (r>mid) lsplit(p<<1|1, l, r);
	}
	void rsplit(int p, int l, int r) {
		if (l>r || !dat(p)) return ;
		if (l<=tl(p) && r>=tr(p)) {rsta[++rtop]=p; return ;}
		int mid=(tl(p)+tr(p))>>1;
		if (r>mid) rsplit(p<<1|1, l, r);
		if (l<=mid) rsplit(p<<1, l, r);
	}
	tpl lsearch(int p, ll x) {
		if (tl(p)==tr(p)) {
			if (!del(p)) cntb(p)=cnt(p), datb(p)=dat(p);
			del(p)=1;
			cnt(p)-=(x-1)/val(p)+1; dat(p)=val(p)*cnt(p);
			for (int t=p/2; t; t>>=1) pushup(t);
			return tpl(tl(p), (x-1)/val(p)+1, val(p)*((x-1)/val(p)+1));
		}
		if (dat(p<<1|1)>x) return lsearch(p<<1|1, x);
		else return lsearch(p<<1, x-dat(p<<1|1));
	}
	int qcnt(int p, int l, int r) {
		if (l>r || !dat(p)) return 0;
		if (l<=tl(p)&&r>=tr(p)) return cnt(p);
		int mid=(tl(p)+tr(p))>>1, ans=0;
		if (l<=mid) ans+=qcnt(p<<1, l, r);
		if (r>mid) ans+=qcnt(p<<1|1, l, r);
		return ans;
	}
	ll qsum(int p, int l, int r) {
		if (l>r || !dat(p)) return 0;
		if (l<=tl(p)&&r>=tr(p)) return dat(p);
		int mid=(tl(p)+tr(p))>>1; ll ans=0;
		if (l<=mid) ans+=qsum(p<<1, l, r);
		if (r>mid) ans+=qsum(p<<1|1, l, r);
		return ans;
	}
	int rsearch(int p, ll x) {
		if (tl(p)==tr(p)) return p;
		if (dat(p<<1)>x) return rsearch(p<<1, x);
		else return rsearch(p<<1, x-dat(p<<1|1));
	}
	int suf(int x) {
		rtop=0;
		rsplit(1, x, usize);
		while (rtop) {
			if (dat[rsta[rtop]])
				return rsearch(rsta[rtop], 0);
			--rtop;
		}
		return -1;
	}
	void remove(int p, int l, int r) {
		if (l<=tl(p)&&r>=tr(p)) {
			if (!del(p)) cntb(p)=cnt(p), datb(p)=dat(p);
			del(p)=1; cnt(p)=dat(p)=0;
			return ;
		}
		int mid=(tl(p)+tr(p))>>1;
		if (l<=mid) remove(p<<1, l, r);
		if (r>mid) remove(p<<1|1, l, r);
		pushup(p);
	}
	void recover(int p, int l, int r) {
		if (l<=tl(p)&&r>=tr(p)) {
			if (del(p)) {del(p)=0; cnt(p)=cntb(p); dat(p)=datb(p);}
			return ;
		}
		int mid=(tl(p)+tr(p))>>1;
		if (l<=mid) recover(p<<1, l, r);
		if (r>mid) recover(p<<1|1, l, r);
		pushup(p);
	}
	void show(int p) {
		if (tl(p)==tr(p)) {cout<<val(p)<<":"<<cnt(p)<<' '; return ;}
		show(p<<1); show(p<<1|1);
	}
	void check(int p) {
		if (del(p)) cout<<"lable alive: "<<p<<' '<<tl(p)<<' '<<tr(p)<<endl;
		if (tl(p)==tr(p)) return ;
		check(p<<1); check(p<<1|1);
	}
	int query(ll s, ll k) {
		// cout<<"query: "<<s<<' '<<k<<endl;
		if (s>=k) return 0;
		int ans=0;
		while (s<k) {
			// cout<<"while: "<<s<<endl;
			int pos=lower_bound(uni+1, uni+usize+1, s)-uni-1, tem=suf(pos+1);
			// cout<<"tem: "<<val(tem)<<' '<<pos<<endl;
			ll dlt=k-s, nxt=k;
			if (~tem) dlt=min(dlt, val(tem)-s+1), nxt=min(nxt, val(tem));
			// cout<<"dlt: "<<dlt<<endl;
			top=0;
			lsplit(1, 1, pos);
			// cout<<"sta: "; for (int i=1; i<=top; ++i) cout<<dat(sta[i])<<' '; cout<<endl;
			while (top) {
				if (dat(sta[top])>=dlt) {
					tpl t=lsearch(sta[top], dlt);
					// cout<<"t: "<<t.fir<<' '<<t.sec<<' '<<t.thr<<endl;
					ans+=t.sec+qcnt(1, t.fir+1, pos);
					// cout<<"qcnt: "<<t.fir+1<<' '<<pos<<' '<<qcnt(1, t.fir+1, pos)<<endl;
					s+=t.thr+qsum(1, t.fir+1, pos);
					// cout<<"qsum: "<<t.fir+1<<' '<<pos<<' '<<qsum(1, t.fir+1, pos)<<endl;
					remove(1, t.fir+1, pos);
					rec[++ctop]=make(t.fir+1, pos);
					rec[++ctop]=make(t.fir, t.fir);
					break;
				}
				dlt-=dat(sta[top--]);
			}
			if (s<=nxt) break;
		}
		while (ctop) recover(1, rec[ctop].fir, rec[ctop].sec), --ctop;
		// check(1);
		// show(1);
		return s>=k?ans:-1;	
	}
	void solve() {
		for (int i=1; i<=n; ++i) {w[i]=read(); uni[++usize]=w[i];}
		m=read();
		for (int i=1; i<=m; ++i) {
			q[i].op=read();
			if (q[i].op==1) {q[i].s=read(); q[i].k=read(); uni[++usize]=q[i].s; uni[++usize]=q[i].k;}
			else {q[i].w=read(); uni[++usize]=q[i].w;}
		}
		sort(uni+1, uni+usize+1);
		usize=unique(uni+1, uni+usize+1)-uni-1;
		for (int i=1; i<=n; ++i) ++cnt2[lower_bound(uni+1, uni+usize+1, w[i])-uni];
		// cout<<"cnt2: "; for (int i=1; i<=usize; ++i) cout<<cnt2[i]<<' '; cout<<endl;
		build(1, 1, usize);
		// show(1); cout<<endl;
		// cout<<"suf: "<<val(suf(8))<<endl;
		for (int i=1; i<=m; ++i) {
			// cout<<"i: "<<i<<endl;
			if (q[i].op==1) printf("%d\n", query(q[i].s, q[i].k));
			else if (q[i].op==2) upd(1, lower_bound(uni+1, uni+usize+1, q[i].w)-uni, 1);
			else upd(1, lower_bound(uni+1, uni+usize+1, q[i].w)-uni, -1);
		}
	}
}

signed main()
{
	freopen("fish.in", "r", stdin);
	freopen("fish.out", "w", stdout);

	n=read();
	// force::solve();
	// task::solve();
	task::solve();

	return 0;
}
posted @ 2021-10-06 19:11  Administrator-09  阅读(6)  评论(0编辑  收藏  举报