题解 大鱼吃小鱼
先放结论:\(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;
}