題解 大魚吃小魚
阿新 • • 發佈:2021-10-07
先放結論:\(O(nlog^3n)\) 可以過,於是下面可以跳過了(彌天大霧
先考慮暴力,顯然是貪心不斷選最大的
發現如果我們令第一個大於當前體積的魚的體積為 \(B\)
則直到當前體積 \(>B\) 時候選集合才會發生變化
一個簡單的思路是將能吃的魚體積排序後二分
因為每這樣做一次一定吃了一條體積大於等於自己的魚
所以體積至少翻倍,只會做log次
但還有插入和刪除的操作,無法方便地得到有序陣列,且每個元素只能產生一次貢獻不好實現
- 當需要動態維護一個有序陣列時:線段樹平衡樹均可,但不把問題抽象成這樣一個裸的問題不好想到
- 當需要動態維護一個有序陣列,還要能在上面做二分時:可以線段樹上二分
- 如果想在一個有序序列上二分(二分值或區間和均可),同時還要支援動態修改,考慮線段樹上二分
- 關於線上段樹上任意位置/區間二分:
可以發現,若是要求線上段樹上直接二分出一個(比如說)字首和小於某個數的位置是容易的
但若是指定了區間 \([x, y]\) 內的字首和小於某個數的位置貌似不好操作
其實你也可以直接算出 \([1, x]\) 的區間和,然後轉化成上面的問題
但是有一個更加巧妙的做法:
區間 \([x, y]\) 線上段樹上是log個區間,所以可以方便地將它們一個log取出來
然後按順序減掉這些區間的區間和,這樣就可以找到最終答案落在哪個區間內
於是在這樣一個區間內二分就很容易了
實現的話可以開個棧存節點 - 關於線上段樹上臨時刪除一個區間:
可以打標記或將要刪的區間扔到棧裡實現,重點在於復原
如果打標記的話刪標記同理,複雜度 \(O(logn)\)
但是將要刪的區間扔到棧裡的話復原是 \(O(log^2n)\) 的(每個區間都要pushup一次)
回到這個題,我們每次找最大的一些魚,將自己吃到候選集合再次發生變化
然後利用區間臨時刪除刪掉這些魚,重複上述過程,直到達到要求的大小位置
複雜度 \(O(nlog^2n)\)
Code:
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define N 500010 #define ll long long #define fir first #define sec second #define make make_pair // #define int long long char buf[1<<21], *p1=buf, *p2=buf; #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++) inline ll read() { ll ans=0, f=1; char c=getchar(); while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();} while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();} return ans*f; } int n, m, q; namespace force{ int top; ll sta[N]; priority_queue<ll> q2; int calc(ll s, ll k) { if (s>=k) return 0; while (q2.size()) q2.pop(); int pos1=1, pos2=lower_bound(sta+1, sta+top+1, s)-sta, cnt=0; while (pos1<pos2) q2.push(sta[pos1++]); while (q2.size()) { s+=q2.top(); q2.pop(); ++cnt; if (s>=k) return cnt; pos2=lower_bound(sta+1, sta+top+1, s)-sta; while (pos1<pos2) q2.push(sta[pos1++]); } assert(s<k); return -1; } void solve() { for (int i=1; i<=n; ++i) sta[++top]=read(); sort(sta+1, sta+n+1); q=read(); ll s, k, w; for (int i=1,op; i<=q; ++i) { op=read(); if (op==1) { s=read(); k=read(); printf("%d\n", calc(s, k)); } else if (op==2) { sta[++top]=read(); sort(sta+1, sta+top+1); } else { w=read(); swap(*lower_bound(sta+1, sta+top+1, w), sta[top]); --top; sort(sta+1, sta+top+1); } } } } namespace task{ int usize, cnt2[N], sta[N], rsta[N], top, rtop, ctop; int tl[N<<2], tr[N<<2], del[N<<2], cnt[N<<2], cntb[N<<2]; ll dat[N<<2], val[N<<2], datb[N<<2], valb[N<<2], uni[N], w[N]; pair<int, int> rec[N]; struct que{int op; ll s, k, w;}q[N]; struct tpl{ ll fir, sec, thr; tpl(){} tpl(ll a, ll b, ll c){fir=a; sec=b; thr=c;} inline void build(ll a, ll b, ll c){fir=a; sec=b; thr=c;} }; #define tl(p) tl[p] #define tr(p) tr[p] #define dat(p) dat[p] #define del(p) del[p] #define cnt(p) cnt[p] #define datb(p) datb[p] #define cntb(p) cntb[p] #define val(p) val[p] #define pushup(p) dat(p)=dat(p<<1)+dat(p<<1|1), cnt(p)=cnt(p<<1)+cnt(p<<1|1) void build(int p, int l, int r) { tl(p)=l; tr(p)=r; if (l==r) {val(p)=uni[l]; cnt(p)=cnt2[l]; dat(p)=val(p)*cnt(p); return ;} int mid=(l+r)>>1; build(p<<1, l, mid); build(p<<1|1, mid+1, r); pushup(p); } void upd(int p, int pos, int dlt) { if (tl(p)==tr(p)) {cnt(p)+=dlt; dat(p)=val(p)*cnt(p); return ;} int mid=(tl(p)+tr(p))>>1; if (pos<=mid) upd(p<<1, pos, dlt); else upd(p<<1|1, pos, dlt); pushup(p); } void lsplit(int p, int l, int r) { if (!dat(p)) return ; if (l<=tl(p) && r>=tr(p)) {sta[++top]=p; return ;} int mid=(tl(p)+tr(p))>>1; if (l<=mid) lsplit(p<<1, l, r); if (r>mid) lsplit(p<<1|1, l, r); } void rsplit(int p, int l, int r) { if (l>r || !dat(p)) return ; if (l<=tl(p) && r>=tr(p)) {rsta[++rtop]=p; return ;} int mid=(tl(p)+tr(p))>>1; if (r>mid) rsplit(p<<1|1, l, r); if (l<=mid) rsplit(p<<1, l, r); } tpl lsearch(int p, ll x) { if (tl(p)==tr(p)) { if (!del(p)) cntb(p)=cnt(p), datb(p)=dat(p); del(p)=1; cnt(p)-=(x-1)/val(p)+1; dat(p)=val(p)*cnt(p); for (int t=p/2; t; t>>=1) pushup(t); return tpl(tl(p), (x-1)/val(p)+1, val(p)*((x-1)/val(p)+1)); } if (dat(p<<1|1)>x) return lsearch(p<<1|1, x); else return lsearch(p<<1, x-dat(p<<1|1)); } int qcnt(int p, int l, int r) { if (l>r || !dat(p)) return 0; if (l<=tl(p)&&r>=tr(p)) return cnt(p); int mid=(tl(p)+tr(p))>>1, ans=0; if (l<=mid) ans+=qcnt(p<<1, l, r); if (r>mid) ans+=qcnt(p<<1|1, l, r); return ans; } ll qsum(int p, int l, int r) { if (l>r || !dat(p)) return 0; if (l<=tl(p)&&r>=tr(p)) return dat(p); int mid=(tl(p)+tr(p))>>1; ll ans=0; if (l<=mid) ans+=qsum(p<<1, l, r); if (r>mid) ans+=qsum(p<<1|1, l, r); return ans; } int rsearch(int p, ll x) { if (tl(p)==tr(p)) return p; if (dat(p<<1)>x) return rsearch(p<<1, x); else return rsearch(p<<1, x-dat(p<<1|1)); } int suf(int x) { rtop=0; rsplit(1, x, usize); while (rtop) { if (dat[rsta[rtop]]) return rsearch(rsta[rtop], 0); --rtop; } return -1; } void remove(int p, int l, int r) { if (l<=tl(p)&&r>=tr(p)) { if (!del(p)) cntb(p)=cnt(p), datb(p)=dat(p); del(p)=1; cnt(p)=dat(p)=0; return ; } int mid=(tl(p)+tr(p))>>1; if (l<=mid) remove(p<<1, l, r); if (r>mid) remove(p<<1|1, l, r); pushup(p); } void recover(int p, int l, int r) { if (l<=tl(p)&&r>=tr(p)) { if (del(p)) {del(p)=0; cnt(p)=cntb(p); dat(p)=datb(p);} return ; } int mid=(tl(p)+tr(p))>>1; if (l<=mid) recover(p<<1, l, r); if (r>mid) recover(p<<1|1, l, r); pushup(p); } void show(int p) { if (tl(p)==tr(p)) {cout<<val(p)<<":"<<cnt(p)<<' '; return ;} show(p<<1); show(p<<1|1); } void check(int p) { if (del(p)) cout<<"lable alive: "<<p<<' '<<tl(p)<<' '<<tr(p)<<endl; if (tl(p)==tr(p)) return ; check(p<<1); check(p<<1|1); } int query(ll s, ll k) { // cout<<"query: "<<s<<' '<<k<<endl; if (s>=k) return 0; int ans=0; while (s<k) { // cout<<"while: "<<s<<endl; int pos=lower_bound(uni+1, uni+usize+1, s)-uni-1, tem=suf(pos+1); // cout<<"tem: "<<val(tem)<<' '<<pos<<endl; ll dlt=k-s, nxt=k; if (~tem) dlt=min(dlt, val(tem)-s+1), nxt=min(nxt, val(tem)); // cout<<"dlt: "<<dlt<<endl; top=0; lsplit(1, 1, pos); // cout<<"sta: "; for (int i=1; i<=top; ++i) cout<<dat(sta[i])<<' '; cout<<endl; while (top) { if (dat(sta[top])>=dlt) { tpl t=lsearch(sta[top], dlt); // cout<<"t: "<<t.fir<<' '<<t.sec<<' '<<t.thr<<endl; ans+=t.sec+qcnt(1, t.fir+1, pos); // cout<<"qcnt: "<<t.fir+1<<' '<<pos<<' '<<qcnt(1, t.fir+1, pos)<<endl; s+=t.thr+qsum(1, t.fir+1, pos); // cout<<"qsum: "<<t.fir+1<<' '<<pos<<' '<<qsum(1, t.fir+1, pos)<<endl; remove(1, t.fir+1, pos); rec[++ctop]=make(t.fir+1, pos); rec[++ctop]=make(t.fir, t.fir); break; } dlt-=dat(sta[top--]); } if (s<=nxt) break; } while (ctop) recover(1, rec[ctop].fir, rec[ctop].sec), --ctop; // check(1); // show(1); return s>=k?ans:-1; } void solve() { for (int i=1; i<=n; ++i) {w[i]=read(); uni[++usize]=w[i];} m=read(); for (int i=1; i<=m; ++i) { q[i].op=read(); if (q[i].op==1) {q[i].s=read(); q[i].k=read(); uni[++usize]=q[i].s; uni[++usize]=q[i].k;} else {q[i].w=read(); uni[++usize]=q[i].w;} } sort(uni+1, uni+usize+1); usize=unique(uni+1, uni+usize+1)-uni-1; for (int i=1; i<=n; ++i) ++cnt2[lower_bound(uni+1, uni+usize+1, w[i])-uni]; // cout<<"cnt2: "; for (int i=1; i<=usize; ++i) cout<<cnt2[i]<<' '; cout<<endl; build(1, 1, usize); // show(1); cout<<endl; // cout<<"suf: "<<val(suf(8))<<endl; for (int i=1; i<=m; ++i) { // cout<<"i: "<<i<<endl; if (q[i].op==1) printf("%d\n", query(q[i].s, q[i].k)); else if (q[i].op==2) upd(1, lower_bound(uni+1, uni+usize+1, q[i].w)-uni, 1); else upd(1, lower_bound(uni+1, uni+usize+1, q[i].w)-uni, -1); } } } signed main() { freopen("fish.in", "r", stdin); freopen("fish.out", "w", stdout); n=read(); // force::solve(); // task::solve(); task::solve(); return 0; }