Best Subsequence 题解
有一个经典 trick。我们先二分答案,然后假设当前二分的值为 \(m\),我们把 \(\le \lfloor\dfrac{m}{2}\rfloor\) 的数看作 \(1\),剩下的看作 \(0\)。
比较显然的是:\(1\) 可以随便放,\(0\) 不能连着放两个。显然我们可以先选所有 \(1\)(这个正确性可以用调整法证明),然后考虑两个 \(1\) 中间的 \(0\) 能不能选。
显然我们选两个 \(1\) 之间最小的 \(0\) 是优的。于是我们设这两个 \(1\) 的较大值为 \(A\),中间 \(0\) 的最小值为 \(B\)。若中间可以选一个 \(0\),就有 \(A+B\le m,m<2\times B\),即 \(m\in [A+B,2\times B)\)。
我们考虑一下我们的二分,check 的时候需要找到区间内所有的 \(1\) 和两个相邻的 \(1\) 之间能放的 \(0\) 的个数。
先考虑 \(1\) 的个数,这个东西其实就是求区间内有多少个数不超过 \(\lfloor\dfrac{m}{2}\rfloor\),相当于求排名,这个直接用主席树做一下就好了。
接下来考虑两个 \(1\) 之间能选多少 \(0\)。
我们考虑在每个点上维护信息 \((x,y,L,R)\),表示当前点是 \(x\)(是一个 \(1\)),下一个 \(1\) 是 \(y\),他们造成贡献的区间是 \([L,R]\)。
发现有一段 \(1\) 会被拆开,这一段就是询问区间内的最后一个 \(1\) 和第一个 \(1\) 之间的那部分(要绕一圈回去),我们先不计算那部分。
考虑判断有多少个 \(m\) 在区间内是困难的,我们可以做一个差分,形如 \([L\le m]-[R+1\le m]\),然后我们也把这个东西扔到主席树里查一下就好了(其实 \(1\) 的个数不需要查排名,直接丢进主席树就好了)。
现在只需要解决跨段的东西,直接找到这一坨东西的最小值加上两端点的最大值看一下会不会造成贡献即可。
注意特判 \(k=1\)。
AC code:
#include<bits/stdc++.h>
#define int long long
#define N 100005
#define pii pair<int,int>
#define pcc pair<char,char>
#define x first
#define y second
#define pct __builtin_popcount
#define mod 998244353
#define inf (2e9+10)
#define pi acos(-1)
#define eps 1e-2
using namespace std;
int T=1,n,q,a[N],p[N],b[N],rt[N];
struct node{
int a,b,c;
bool operator<(const node &t)const{
if(a!=t.a)return a<t.a;
if(b!=t.b)return b<t.b;
return c<t.c;
}
};
vector<node>e;
struct sgt{
int tr[N<<2];
void pushup(int u){
tr[u]=min(tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r){
if(l==r){
tr[u]=a[l];
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
int qry(int u,int l,int r,int L,int R){
if(l>=L&&r<=R)return tr[u];
int mid=l+r>>1;
int res=inf;
if(L<=mid)res=min(res,qry(u<<1,l,mid,L,R));
if(R>mid)res=min(res,qry(u<<1|1,mid+1,r,L,R));
return res;
}
int qryl(int u,int l,int r,int x,int v){
if(tr[u]>v)return n+1;
if(l==r)return l;
int mid=l+r>>1;
if(x>mid)return qryl(u<<1|1,mid+1,r,x,v);
int res=qryl(u<<1,l,mid,x,v);
if(res!=n+1)return res;
else return qryl(u<<1|1,mid+1,r,x,v);
}
int qryr(int u,int l,int r,int x,int v){
if(tr[u]>v)return 0;
if(l==r)return l;
int mid=l+r>>1;
if(x<=mid)return qryr(u<<1,l,mid,x,v);
int res=qryr(u<<1|1,mid+1,r,x,v);
if(res!=0)return res;
else return qryr(u<<1,l,mid,x,v);
}
}tr;
struct dsgt{
int ls[N<<6],rs[N<<6],s[N<<6],val[N<<6],cnt,tot;
void pushup(int &u,int las,int l,int r,int x,int v){
u=++cnt;
s[u]=s[las]+v;
ls[u]=ls[las];
rs[u]=rs[las];
if(l==r)return;
int mid=l+r>>1;
if(x<=mid)pushup(ls[u],ls[las],l,mid,x,v);
else pushup(rs[u],rs[las],mid+1,r,x,v);
}
void build(){
sort(e.begin(),e.end());
int now=0;
for(auto it:e){
if(!tot||val[tot]!=it.a)val[++tot]=it.a;
pushup(rt[tot],now,1,n,it.b,it.c);
now=rt[tot];
}
}
int qry(int u,int l,int r,int L,int R){
if(!u)return 0;
if(l>=L&&r<=R)return s[u];
int mid=l+r>>1;
int res=0;
if(L<=mid)res+=qry(ls[u],l,mid,L,R);
if(R>mid)res+=qry(rs[u],mid+1,r,L,R);
return res;
}
int find(int v,int L,int R){
int u=upper_bound(val+1,val+tot+1,v)-val;
return qry(rt[u-1],1,n,L,R);
}
}dtr;
void solve(int cs){
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>a[i];
p[i]=i;
}
sort(p+1,p+n+1,[&](int x,int y){
return a[x]<a[y];
});
tr.build(1,1,n);
set<int>s;
for(int cur=1;cur<=n;cur++){
int i=p[cur];
auto it=s.lower_bound(i);
e.push_back({a[i]*2,i,1});
int l=0,r=n+1;
if(it!=s.end())r=*it;
if(it!=s.begin())l=*(--it);
if(l!=0){
if(b[l]<a[i]*2){
e.push_back({b[l],l,1});
e.push_back({a[i]*2,l,-1});
}
b[l]=inf;
if(l+1<i)b[l]=tr.qry(1,1,n,l+1,i-1)+a[i];
}
b[i]=inf;
if(i+1<r)b[i]=tr.qry(1,1,n,i+1,r-1)+a[i];
s.insert(i);
}
dtr.build();
while(q--){
int l,r,k;
cin>>l>>r>>k;
int cur=tr.qry(1,1,n,l,r)*2;
if(k==1){
cout<<cur<<'\n';
continue;
}
int L=cur,R=inf-10,res=R;
while(L<=R){
int mid=L+R>>1;
int pl=tr.qryl(1,1,n,l,mid/2);
int pr=tr.qryr(1,1,n,r,mid/2);
int sum=dtr.find(mid,pl,pr-1)+1;
int now=inf;
if(l<pl)now=min(now,tr.qry(1,1,n,l,pl-1));
if(r>pr)now=min(now,tr.qry(1,1,n,pr+1,r));
if(now<inf&&now+max(a[pl],a[pr])<=mid)sum++;
if(sum>=k){
res=mid;
R=mid-1;
}
else L=mid+1;
}
cout<<res<<'\n';
}
}
void solution(){
/*
nothing here
*/
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// init();
// cin>>T;
for(int cs=1;cs<=T;cs++){
solve(cs);
}
return 0;
}