题解 魔法球

传送门

考虑二分答案为 \(mid\)
反证不打破的选前 \(k\) 大一定更优
反证剩下的每次一定选最大的打破
反证每次一定将当前打破的优先分配给前 \(a_i-mid\) 小的
然后发现一定存在一种方案每次分配后每个球的魔力值按原顺序(在打破第一个球前升序排序)单调不降
具体地,值不同时分配给值较小的,值相同时分配给编号大的
发现打破一个球后魔力值最大值最多增加 1
可以维护出值相同的连续段,每次树状数组上二分出分界线在哪个段中,指针维护当前最大值所在连续段
这样可以做到 \(O(n\log^2n)\),精细实现可以卡过

然后这个做法是容易优化到 \(O(n\log n)\)
考虑我们要二分出最大值所在连续段
而对一个连续段操作相当于删除这个连续段并增加相邻两个连续段的长度
那么对所有不为空的连续段建立双向链表
每次临界值所在的连续段的位置变化量是均摊 \(O(n)\)
这样就是 \(O(n\log n)\)

然后还有一个神奇做法:
从大到小依次考虑第 \(i\) 个位置,check 这样一个东西:
if (sum>(ll)(mid+i-1)*(n-mid-i+1)) return 0;
也就是在考虑左侧能否容纳右侧传递过来的所有值
正确性考虑????好像又假了,等沈老师打完 AGC 去问问

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define fir first
#define sec second
#define ll long long
//#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 int read() {
	int 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;
int a[N];

// namespace force{
// 	int sta[N], top;
// 	bool check(int mid) {
// 		sort(a+1, a+n+1); top=0;
// 		for (int i=1; i<=n-mid; ++i) sta[++top]=a[i];
// 		while (top) {
// 			sort(sta+1, sta+top+1, [](int a, int b){return a>b;});
// 			if (sta[1]>=mid+top) return 0;
// 			else for (int i=top; i>=top-(sta[1]-mid)+1; --i) ++sta[i];
// 			swap(sta[1], sta[top--]);
// 		}
// 		return 1;
// 	}
// 	void solve() {
// 		int l=1, r=n, mid;
// 		while (l<=r) {
// 			mid=(l+r)>>1;
// 			if (check(mid)) r=mid-1;
// 			else l=mid+1;
// 		}
// 		printf("%d\n", r+1);
// 	}
// }

// namespace task1{
// 	int sta[N], buc[N], ls[N], rs[N], top;
// 	bool check(int mid) {
// 		sort(a+1, a+n+1); top=0;
// 		for (int i=1; i<=n-mid; ++i) sta[++top]=a[i];
// 		while (top) {
// 			if (sta[top]>=mid+top) return 0;
// 			for (int i=1; i<=mid+top; ++i) buc[i]=ls[i]=rs[i]=0;
// 			for (int i=1; i<=top; ++i) ++buc[sta[i]], rs[sta[i]]=i;
// 			for (int i=top; i; --i) ls[sta[i]]=i;
// 			int cnt=(sta[top]-mid);
// 			for (int i=1; cnt>0; ++i) {
// 				if (cnt>=buc[i]) {
// 					for (int j=ls[i]; j<=rs[i]; ++j) ++sta[j];
// 					cnt-=buc[i];
// 				}
// 				else {
// 					for (int j=rs[i]; j>=rs[i]-cnt+1; --j) ++sta[j];
// 					break;
// 				}
// 			}
// 			--top;
// 		}
// 		return 1;
// 	}
// 	void solve() {
// 		int l=1, r=n, mid;
// 		while (l<=r) {
// 			mid=(l+r)>>1;
// 			if (check(mid)) r=mid-1;
// 			else l=mid+1;
// 		}
// 		printf("%d\n", r+1);
// 	}
// }

// namespace task2{
// 	int sta[N], top;
// 	struct range{int l, r; bool del;}rg[N];
// 	int findnxt(int i) {for (++i; i<=n; ++i) if (!rg[i].del) return i; return -1;}
// 	int findpre(int i) {for (--i; i; --i) if (!rg[i].del) break; return i;}
// 	bool check(int mid) {
// 		// cout<<"check: "<<mid<<endl;
// 		sort(a+1, a+n+1); top=0;
// 		for (int i=1; i<=n-mid; ++i) sta[++top]=a[i];
// 		for (int i=1,p2=0,p1; i<=mid+top; ++i) {
// 			p1=p2+1;
// 			while (p2<top&&sta[p2+1]==i) ++p2;
// 			rg[i]={p1, p2, 0};
// 		}
// 		if (sta[top]>=mid+top) return 0;
// 		// cout<<"mid: "<<mid<<endl;
// 		// cout<<"a: "; for (int i=1; i<=n; ++i) cout<<a[i]<<' '; cout<<endl;
// 		// cout<<"sta: "; for (int i=1; i<=top; ++i) cout<<sta[i]<<' '; cout<<endl;
// 		// cout<<"rg: "; for (int i=1; i<=n; ++i) cout<<"("<<rg[i].l<<','<<rg[i].r<<") "; cout<<endl;
// 		int now=n, val=n;
// 		while (top) {
// 			// cout<<"top: "<<top<<endl;
// 			// cout<<"rg: "; for (int i=1; i<=n; ++i) cout<<"("<<rg[i].l<<','<<rg[i].r<<','<<rg[i].del<<") "; cout<<endl;
// 			int nxt=findnxt(now);
// 			if (~nxt) now=nxt, ++val;
// 			while (rg[now].del||rg[now].l>rg[now].r)
// 				if (rg[now].del) --now;
// 				else --now, --val;
// 			// cout<<"now: "<<now<<' '<<val<<endl;
// 			if (val>=mid+top) return 0;
// 			int tem=val-mid;
// 			for (int i=1; tem>0; ++i) if (!rg[i].del&&rg[i].l<=rg[i].r) {
// 				// cout<<"i: "<<i<<endl;
// 				int siz=rg[i].r-rg[i].l+1;
// 				if (siz<tem) tem-=siz;
// 				else {
// 					int pos=rg[i].r-tem+1;
// 					int nxt=findnxt(i);
// 					rg[nxt].l=pos;
// 					int pre=findpre(i);
// 					if (pre) rg[pre].r=pos-1, rg[i].del=1;
// 					else rg[i].r=pos-1;
// 					break;
// 				}
// 			}
// 			--rg[now].r;
// 			// rg[now+1]={rg[now].r, rg[now].r+1, 0};
// 			--top;
// 			// cout<<endl;
// 		}
// 		// cout<<"return 1"<<endl;
// 		return 1;
// 	}
// 	void solve() {
// 		int l=1, r=n, mid;
// 		while (l<=r) {
// 			mid=(l+r)>>1;
// 			if (check(mid)) r=mid-1;
// 			else l=mid+1;
// 		}
// 		printf("%d\n", r+1);
// 		// check(3);
// 	}
// }

namespace task3{
	int bit[N], sta[N], top, lst=1;
	struct node{int pre, nxt;}nod[N];
	struct range{int l, r; bool del;}rg[N];
	// int findnxt(int i) {for (++i; i<=n; ++i) if (!rg[i].del) return i; return -1;}
	// int findpre(int i) {for (--i; i; --i) if (!rg[i].del) break; return i;}
	inline int findnxt(int i) {return nod[i].nxt;}
	inline int findpre(int i) {return nod[i].pre;}
	inline void add(int i, int dat) {for (; i<=n; i+=i&-i) bit[i]+=dat;}
	inline int query(int i) {int ans=0; for (; i; i-=i&-i) ans+=bit[i]; return ans;}
	inline int query(int l, int r) {
		int ans=0; --l;
		while (r>l) ans+=bit[r], r-=r&-r;
		while (l>r) ans-=bit[l], l-=l&-l;
		return ans;
	}
	inline void del(int i) {
		rg[i].del=1;
		if (~nod[i].pre) nod[nod[i].pre].nxt=nod[i].nxt;
		if (~nod[i].nxt) nod[nod[i].nxt].pre=nod[i].pre;
		// add(i, query(i-1)-query(i));
	}
	inline int findfir() {for (; ; ++lst) if (!rg[lst].del) return lst;}
	bool check(int mid) {
		// cout<<"check: "<<mid<<endl;
		top=0;
		for (int i=0; i<=n; ++i) bit[i]=0;
		for (int i=1; i<=n-mid; ++i) sta[++top]=a[i];
		if (sta[top]>=mid+top) return 0;
		for (int i=1,p2=0,p1; i<=mid+top; ++i) {
			p1=p2+1;
			while (p2<top&&sta[p2+1]==i) ++p2;
			rg[i]={p1, p2, 0};
			if (p1<=p2) add(i, p2-p1+1);
		}
		// cout<<"mid: "<<mid<<endl;
		// cout<<"a: "; for (int i=1; i<=n; ++i) cout<<a[i]<<' '; cout<<endl;
		// cout<<"sta: "; for (int i=1; i<=top; ++i) cout<<sta[i]<<' '; cout<<endl;
		// cout<<"rg: "; for (int i=1; i<=n; ++i) cout<<"("<<rg[i].l<<','<<rg[i].r<<") "; cout<<endl;
		for (int i=1; i<n; ++i) nod[i].nxt=i+1; nod[n].nxt=-1;
		for (int i=2; i<=n; ++i) nod[i].pre=i-1; nod[1].pre=-1;
		int now=n, val=n;
		while (top) {
			// cout<<"top: "<<top<<endl;
			// cout<<"rg: "; for (int i=1; i<=n; ++i) cout<<"("<<rg[i].l<<','<<rg[i].r<<','<<rg[i].del<<") "; cout<<endl;
			int nxt=findnxt(now);
			if (~nxt) now=nxt, ++val;
			while (rg[now].l>rg[now].r) now=findpre(now), --val;
			// while (rg[now].del||rg[now].l>rg[now].r)
				// if (rg[now].del) --now;
				// else --now, --val;
			// cout<<"now: "<<now<<' '<<val<<endl;
			if (val>=mid+top) return 0;
			// fflush(stdout);
			// for (int i=1; i<=n; ++i) if (rg[i].del) {
			// 	if (query(i)-query(i-1)) cout<<"wrong: "<<i<<' '<<query(i)-query(i-1)<<endl;
			// 	assert(query(i)-query(i-1)==0);
			// }
			// for (int i=1; i<=n; ++i) if (!rg[i].del) {
			// 	if (rg[i].l>rg[i].r) assert(query(i)-query(i-1)==0);
			// 	else {
			// 		if (query(i)-query(i-1)!=rg[i].r-rg[i].l+1) cout<<"wrong: "<<i<<' '<<rg[i].l<<' '<<rg[i].r<<' '<<query(i)-query(i-1)<<endl;
			// 		assert(query(i)-query(i-1)==rg[i].r-rg[i].l+1);
			// 	}
			// }
			int tem=val-mid, pos=0;
			// cout<<"tem: "<<tem<<endl;
			if (tem>0) {
				for (int i=20; ~i; --i) if ((pos|(1<<i))<=n && tem>bit[pos|(1<<i)])
					tem-=bit[pos|=(1<<i)];
				if (pos) pos=findnxt(pos);
				else pos=findfir();
				// cout<<"find: "<<pos<<endl;
				int siz=rg[pos].r-rg[pos].l+1;
				// assert(query(pos)-query(pos-1)==siz);
				int lim=rg[pos].r-tem+1;
				// cout<<"lr: "<<rg[pos].l<<' '<<rg[pos].r<<' '<<lim<<' '<<tem<<endl;
				int nxt=findnxt(pos), lst=rg[nxt].r-rg[nxt].l+1;
				rg[nxt].l=lim;
				// cout<<query(pos)-query(pos-1)<<endl;
				// add(nxt, tem);
				// add(pos, -tem);
				add(nxt, -lst+(rg[nxt].r-rg[nxt].l+1));
				// cout<<query(pos)-query(pos-1)<<endl;
				int pre=findpre(pos);
				// cout<<"nxt&pre: "<<nxt<<' '<<pre<<endl;
				// cout<<"nxt: "<<rg[nxt].l<<' '<<rg[nxt].r<<endl;
				// assert(!rg[nxt].del&&!rg[pre].del);
				if (~pre) rg[pre].r=lim-1, add(pre, siz-tem), add(pos, -siz), del(pos); //, assert(query(pos)-query(pos-1)==0);
				else if (now!=pos) rg[pos].r=lim-1, add(pos, -tem); //, assert(query(pos)-query(pos-1)==siz-tem);
				else rg[pos].r=lim-1, add(pos, -tem-(tem!=siz));
				// cout<<query(pos)-query(pos-1)<<endl;
			}
			--rg[now].r;
			if (now!=pos && !rg[now].del) add(now, -1);
			--top;
			// cout<<endl;
		}
		// cout<<"return 1"<<endl;
		return 1;
	}
	void solve() {
		sort(a+1, a+n+1);
		int l=1, r=n, mid;
		while (l<=r) {
			mid=(l+r)>>1;
			if (check(mid)) r=mid-1;
			else l=mid+1;
		}
		printf("%d\n", r+1);
		// check(5);
	}
}

namespace task4{
	bool check(int mid) {
		// cout<<"check: "<<mid<<endl;
		ll sum=0;
		for (int i=n-mid; i; --i) {
			sum+=a[i];
			if (sum>(ll)(mid+i-1)*(n-mid-i+1)) return 0;
		}
		return 1;
	}
	void solve() {
		sort(a+1, a+n+1);
		int l=1, r=n, mid;
		while (l<=r) {
			mid=(l+r)>>1;
			if (check(mid)) r=mid-1;
			else l=mid+1;
		}
		printf("%d\n", r+1);
		// check(5);
	}
}

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

	int T=read();
	while (T--) {
		n=read();
		for (int i=1; i<=n; ++i) a[i]=read();
		// force::solve();
		// task1::solve();	
		// task2::solve();
		// task3::solve();
		// if (n<=100) force::solve();
		// else task3::solve();
		task4::solve();
	}

	return 0;
}
posted @ 2022-05-07 21:48  Administrator-09  阅读(5)  评论(0编辑  收藏  举报