zoukankan      html  css  js  c++  java
  • [提高组集训2021] 大套子

    一、题目

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

    有下列三种可能的事件:

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

    (nleq 3cdot 10^5,qleq 10^5)

    二、解法

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

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

    对于所有小于 (x) 的人类,我们把它们都放在权值线段树上,每次在线段树上二分一段后缀即可,时间复杂度 (O(nlog^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
    ",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();
    		}
    	}
    }
    
  • 相关阅读:
    使用DataReader
    使用Dataset
    Command
    Ado.net对象
    集合数据源
    Label 表达式绑定
    输出二进制图像
    Application 可以存储全局变量
    字符串处理
    ?:叫条件运算符
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15369362.html
Copyright © 2011-2022 走看看