Loading

【题解】[JOISC 2021 Day1] フードコート

先膜拜楼上 7k 代码的神仙。

这里提供一个轻工业的做法。

首先考虑没有离开操作,那么对于每个询问,我们只需要知道最早在哪一次操作队列 \(A\) 的大小 \(\ge B\)

这可以对所有询问离线,然后将每个询问挂在对应的位置,用线段树维护区间中询问的最小值。

这时一个加入操作,等价于区间减,当某个位置 \(\le 0\) 时,该位置对应询问的答案就是当前操作的标号 \(C\) 。注意一个位置可以挂多个询问,开 vector 记录。

一个询问最多只有一次从 \(>0\)\(\le 0\) ,所以均摊线段树的时间复杂度是 \(\mathcal{O}((N+Q)\log N)\) 。离线后线段树二分应该也可以,本质不变。

现在考虑存在离开操作,如果我们知道当前询问之前队列删除了多少个数,则可以把问题转换为没有离开操作。因为假定删除了 \(k\) 个数,相当于查询第 \(B+k\) 个加入队列的数 。

继续推导,如果我们知道当前队列的大小,还知道有多少数入过队,就能得到出队的数的个数。

后者可以直接树状数组维护区间加,单点查询。

前者需要支持

  • 区间加
  • 区间减
  • 区间对 \(0\)\(\max\)

直接套区间最值线段树即可。

具体做法是对每个位置维护二元标记 \((p,q)\),注意这两个元素是一起的,表示区间里的数 \(+p\) 后对 \(q\)\(\max\)

合并两个标记 \((u,v),(p,q)\) ,注意有现后顺序,手算一下得到标记 \((u+p,\max\{v+p,q\})\)

时间复杂度 \(\mathcal{O}((N+Q)\log N)\) ,略优于线段树二分的 \(\mathcal{O}((N+Q)\log (N+Q))\)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 250005
#define inf 0x3f3f3f3f3f3f3f3fLL
#define int long long
typedef long long ll;
using namespace std;
int n,m,ans[N],idx,c[N];
inline void add(int x,int val){for(;x<=n;x+=x&-x)c[x]+=val;}
inline int ask(int x){int sum=0;for(;x;x-=x&-x)sum+=c[x];return sum;}
#define L a[x].l
#define R a[x].r
#define ls (x<<1)
#define rs (ls|1)
#define T a[x].tag
namespace seg1{
	struct node{
		int l,r;ll p,q;
	}a[N<<2];
	void build(int x,int l,int r){
		L=l;R=r;a[x].p=a[x].q=0;
		if(l==r)return;
		int mid=(l+r)>>1;
		build(ls,l,mid);build(rs,mid+1,r);
	}
	void pushup(int x,ll u,ll v){a[x].p+=u;a[x].q=max(v,a[x].q+u);}
	void down(int x){if(a[x].p||a[x].q)pushup(ls,a[x].p,a[x].q),pushup(rs,a[x].p,a[x].q),a[x].p=a[x].q=0;}
	void add(int x,int l,int r,int val){
		if(L>=l&&R<=r)pushup(x,val,0);
		else{
			down(x);int mid=(L+R)>>1;
			if(mid>=l)add(ls,l,r,val);
			if(mid<r)add(rs,l,r,val);
		}
	}
	ll ask(int x,int pos){
		if(L==R)return max(a[x].p,a[x].q);
		else{
			down(x);int mid=(L+R)>>1;
			if(mid>=pos)return ask(ls,pos);
			return ask(rs,pos);
		}
	}
}
namespace seg2{
	struct node{
		int l,r,mn,tag;
	}a[N<<2];
	#define S a[x].mn
	vector<pair<int,int> >u[N];int st[N];
	void build(int x,int l,int r){
		L=l,R=r,T=0;
		if(l==r){if(u[l].size())S=u[l][0].first;else S=inf;}
		else{
			int mid=(l+r)>>1;
			build(ls,l,mid);
			build(rs,mid+1,r);
			S=min(a[ls].mn,a[rs].mn);
		}
	}
	void pushup(int x,int val){T+=val;S+=val;}
	void down(int x){if(T)pushup(ls,T),pushup(rs,T),T=0;}
	void add(int x,int l,int r,int val){
		if(L>=l&&R<=r)pushup(x,val);
		else{
			down(x);int mid=(L+R)>>1;
			if(mid>=l)add(ls,l,r,val);
			if(mid<r)add(rs,l,r,val);
			S=min(a[ls].mn,a[rs].mn);
		}
	}
	void maintain(int x,int col){
		if(S>0)return;
		if(L==R){
			while(st[L]<(int)u[L].size()&&S<=0){
				ans[u[L][st[L]++].second]=col;
				if(st[L]==(int)u[L].size())S=inf;
				else S+=u[L][st[L]].first-u[L][st[L]-1].first;
			}
		}
		else down(x),maintain(ls,col),maintain(rs,col),S=min(a[ls].mn,a[rs].mn);
	}
}
struct ope{int op,l,r,x,y;}q[N];
signed main(){
	scanf("%lld%lld",&n,&m);scanf("%lld",&m);
	seg1::build(1,1,n);puts("No Copy");
	rep(i,1,m){
		scanf("%lld%lld%lld",&q[i].op,&q[i].l,&q[i].r);
		if(q[i].op==1){
			scanf("%lld%lld",&q[i].x,&q[i].y);
			seg1::add(1,q[i].l,q[i].r,q[i].y);
			add(q[i].l,q[i].y);add(q[i].r+1,-q[i].y);
		}
		else if(q[i].op==2){
			scanf("%lld",&q[i].y);
			seg1::add(1,q[i].l,q[i].r,-q[i].y);
		}
		else{
			++idx;int now=seg1::ask(1,q[i].l);
			if(now>=q[i].r)seg2::u[q[i].l].push_back(make_pair(ask(q[i].l)-now+q[i].r,idx));
		}
	}
	rep(i,1,n)sort(seg2::u[i].begin(),seg2::u[i].end());
	seg2::build(1,1,n);
	rep(i,1,m)if(q[i].op==1){
		seg2::add(1,q[i].l,q[i].r,-q[i].y);
		seg2::maintain(1,q[i].x);
	}
	rep(i,1,idx)printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2021-10-04 17:11  7KByte  阅读(102)  评论(0编辑  收藏  举报