【題解】[JOISC 2021 Day1] フードコート
阿新 • • 發佈:2021-10-04
先膜拜樓上 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)\)
時間複雜度 \(\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; }