1. 程式人生 > 其它 >【題解】[JOISC 2021 Day1] フードコート

【題解】[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;
}