[提高组集训2021] 大套子

一、题目

校长有一个体积为 \(x\) 的大套子,现在有 \(n\) 条人类,如果套子的体积严格大于人类的体积 \(y\),那么校长就会把这个人类装在套子里,套子的体积就会增加 \(y\)

有下列三种可能的时间:

  • 校长得到了一个大小为 \(x\) 的套子,他想让套子的大小至少变成 \(y\),如果可以输出步数,否则输出 \(-1\)
  • 新来了一条体积为 \(y\) 的人类
  • 离开了一条体积为 \(y\) 的人类

\(n\leq 3\cdot 10^5,q\leq 10^5\)

二、解法

贪心的做法是每次套能套的最大人类,但暴力进行此过程显然会 \(\tt T\)

这个东西也不是很好维护,所以要想一些在线的做法,我们想要让套的次数是 \(\tt log\) 级的,所以我们一次套多点呗。具体来说我们套到能套一个更大的人类为止,那么这样做两次可以让套子大小翻倍。

对于所有小于 \(x\) 的人类,我们把它们都放在权值线段树上,每次在线段树上二分一段后缀即可,时间复杂度 \(O(n\log^2n)\)

难点是实现细节,二分的时候如果我们要套人类,直接把这个节点的信息清零,以后不访问信息被清零的节点即可(不需要打标记),但是因为询问独立所以我们要回退,拿个栈记录一下我们所有在线段树上的修改即可。

三、总结

对于这类在线处理的增量型问题,考虑怎样才能只做 \(\log n\) 次。

#pragma GCC optimize(2)
#include <cstdio>
#include <stack>
#include <map>
using namespace std;
#define ll long long
#define int long long
const int N = 13000005;
const ll up = 1e12;
ll read()
{
	ll x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
ll n,m,sum[N],num[N];int rt,ls[N],rs[N],fa[N];
ll cnt,ans,x,y,rm;map<ll,ll> mp;
struct node{ll x,y,z;};stack<node> bk;
void upload(int x)
{
	sum[x]=sum[ls[x]]+sum[rs[x]];
	num[x]=num[ls[x]]+num[rs[x]];
	fa[ls[x]]=fa[rs[x]]=x;
}
void ins(int &x,ll l,ll r,ll id,ll f)
{
	if(!x) x=++cnt;
	num[x]+=f;
	sum[x]+=f*id;
	if(l==r) return ;
	ll mid=(l+r)>>1;
	if(mid>=id) ins(ls[x],l,mid,id,f);
	else ins(rs[x],mid+1,r,id,f);
}
void add(int &i,ll l,ll r)
{
	if(l>=x || !sum[i] || !i || rm<=0) return ;
	if(l==r)
	{
		ll tmp=min((rm+l-1)/l,num[i]);
		rm-=tmp*l;x+=tmp*l;ans+=tmp;
		bk.push(node{i,sum[i],num[i]});
		sum[i]-=tmp*l;num[i]-=tmp;
		return ;
	}
	ll mid=(l+r)>>1;
	if(r<=x && sum[i]<=rm)
	{
		rm-=sum[i];x+=sum[i];ans+=num[i];
		bk.push(node{i,sum[i],num[i]});
		sum[i]=num[i]=0;
		return ;
	}
	add(rs[i],mid+1,r);
	add(ls[i],l,mid);
	upload(i);
}
void work()
{
	ans=0;
	while(x<y)
	{
		map<ll,ll>::iterator it=mp.lower_bound(x);
		ll to=y-1;
		if(it!=mp.end()) to=min(to,it->first);
		rm=to-x+1;
		add(rt,1,up);
		if(rm>0) break;
	}
	while(!bk.empty())
	{
		ll t=bk.top().x;
		sum[t]=bk.top().y;
		num[t]=bk.top().z;
		while(t!=rt)
		{
			t=fa[t];
			upload(t);
		}
		bk.pop();
	}
	if(x<y) puts("-1");
	else printf("%lld\n",ans);
}
signed main()
{
	freopen("fish.in","r",stdin);
	freopen("fish.out","w",stdout);
	n=read();
	for(ll i=1;i<=n;i++)
	{
		ll x=read();mp[x]++;
		ins(rt,1,up,x,1);
	}
	m=read();
	while(m--)
	{
		ll op=read();x=read();
		if(op==2)
		{
			mp[x]++;
			ins(rt,1,up,x,1);
		}
		if(op==3)
		{
			mp[x]--;
			if(!mp[x]) mp.erase(x);
			ins(rt,1,up,x,-1);
		}
		if(op==1)
		{
			y=read();work();
		}
	}
}
上一篇:HTML学习第二天


下一篇:socket套接字编程 HTTP协议