UNR #6 稳健型选手
神题。模拟赛搬了这个。
显然有一个 \(n^2q\) 的背包做法,考虑怎么继续优化。
sub1
首先贪心地,如果区间长度是奇数,那么最后一个数肯定最后一个取,然后变为长度偶数地情况
考虑将用 \(\rm 01\) 序列 \(b\) 表示所取得数。
合法的充要条件是 \(\forall i\sum\limits_{j=1}^ib_j\le \lfloor\frac{i}{2}\rfloor\)。
考虑从前往后扫,用集合 \(s\) 维护已经取了的数,每次加进来两个数 \(x,y\),如果都取那么集合大小会变成 \(\frac{i}{2}+1\),所以再从集合中删掉最小的数。
优化至 \(O(nq\log n)\),可以获得 \(40'\)。
sub2
上述贪心是从左边向右边扩展,我们考虑从右边向左边扩展。
转化充要条件: 前 \(2i\) 个取了 \(\le i\) 个 \(\to\) 后 \(2i\) 个取了 \(\ge i\) 个。
所以我们用集合 \(s\) 维护没有取得数,然后每次加进来 \(x,y\),现在集合大小为 \(\frac{i}{2}\),至少要再取一个,所以我们再将集合中最大的数删去。同样可以做到 \(O(nq\log n)\)。
sub3
既然我们已经可以向左边和右边扩展,相当于可以在 \(O(\log n )\) 时间内向左边和右边扩展,使用不删除莫队可以做到 \(n\sqrt q\log n\)。 使用科技压位 tris/web tree 可以将这里的 \(log n\)变成 \(\log_w n\) 或 \(\log\log n\) 就过了
sub4
可以向右和向左扩展,我们想到猫树分治,并发现这些信息可以合并。
使用主席树维护 \([l,mid]\) 的后缀取了哪些数,和 \([mid+1,r]\) 的前缀没取哪些数。然后我们发现可以左边已经取了的数不取,去取右边的数,可以二分 \(k\),满足左边第 \(k\) 小 <右边第 \(k\) 大。可以做到 \(O((n+q)\log^2 n)\)。可以继续优化,因为左边第 \(k\) 小不取,相当于同时取左边和右边 \(\ge x\) 的数,可以线段树二分变成 \(O(n\log^2n+q\log n)\)
code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
inline int read(){
char ch=getchar();
while(!isdigit(ch) && ch!='-') ch=getchar();
int x=0,ff=1; if(ch=='-') ff=-1,ch=getchar();
while(isdigit(ch)) x=(x<<3) + (x<<1) + (ch^48),ch=getchar();
return x*ff;
}
const int N=2e5+5,inf=1e9;
int n,Qm,a[N],tot,rT[N]; LL sm[N];
struct qdate{
int l,r;
} Q[N]; LL ans[N];
struct tnode{
int lc,rc,c; LL s;
} tr[N<<6];
void Mdf(int &p,int pp,int L,int R,int kx){
tr[p=++tot]=tr[pp]; tr[p].c++; tr[p].s+=kx;
if(L==R) return ;
int mid=(L+R)/2;
if(kx<=mid) Mdf(tr[p].lc,tr[pp].lc,L,mid,kx);
else Mdf(tr[p].rc,tr[pp].rc,mid+1,R,kx);
}
LL Qry(int p,int pp,int L,int R,int k){
if(L==R) return 1ll*k*L;
int mid=(L+R)/2; int rp=tr[p].rc,rpp=tr[pp].rc,bf=tr[rp].c+tr[rpp].c;
if(bf>=k) return Qry(rp,rpp,mid+1,R,k);
else return tr[rp].s+tr[rpp].s+Qry(tr[p].lc,tr[pp].lc,L,mid,k-bf);
}
LL solve(int l,int r){
if(l==r) return a[l];
LL res=0;
if((r-l+1)%2==1) res+=a[r--];
priority_queue<int,vector<int>,greater<int> > qt;
for(int i=l;i<=r;i+=2){
qt.push(a[i]); qt.push(a[i+1]);
qt.pop();
}
while(!qt.empty()) res+=qt.top(),qt.pop();
return res;
}
void solve(int L,int R,vector<int> q){
if(q.empty()) return ;
if(L==R){
for(int i:q) ans[i]=a[L];
return ;
}
int mid=(L+R)/2; vector<int> Lt,Rt,qq[2];
for(int i:q){
if(Q[i].r<=mid) Lt.push_back(i);
else if(Q[i].l>mid) Rt.push_back(i);
else qq[Q[i].l&1].push_back(i);
}
for(int o=0;o<2;o++){
int nk=((mid&1)==o)?mid:mid-1;
if(qq[o].empty()) continue;
tot=0; int las=0;
multiset<int> sh; sh.clear(); sh.insert(0);
for(int i=nk;i>=L;i-=2){
int u=a[i],v=a[i+1];
if(u<v) swap(u,v);
if(*sh.rbegin()>u){
Mdf(rT[i],las,1,inf,*sh.rbegin());
sh.erase(--sh.end());
sh.insert(u); sh.insert(v);
}
else
sh.insert(v), Mdf(rT[i],las,1,inf,u);
las=rT[i];
}
sh.clear(); sh.insert(inf); las=0;
LL ss=0;
for(int i=nk+2;i<R;i+=2){
int u=a[i],v=a[i+1];
if(u<v) swap(u,v);
if(*sh.begin()<v){
Mdf(rT[i],las,1,inf,*sh.begin());
ss+=u+v-*sh.begin();
sh.erase(sh.begin()); sh.insert(u); sh.insert(v);
}
else
Mdf(rT[i],las,1,inf,v),ss+=u,sh.insert(u);
las=rT[i]; sm[i]=ss;
}
for(int i:qq[o]){
int ql=Q[i].l,qr=Q[i].r;
if((qr&1)==o) ans[i]+=a[qr--];
--qr; ql=rT[ql];
if(qr>nk) ans[i]+=sm[qr],qr=rT[qr];
else qr=0;
ans[i]+=Qry(ql,qr,1,inf,tr[ql].c);
}
}
solve(L,mid,Lt); solve(mid+1,R,Rt);
}
int main(){
n=read(); Qm=read();
for(int i=1;i<=n;i++) a[i]=read();
vector<int> q; q.clear();
for(int i=1;i<=Qm;i++) Q[i]=(qdate){read(),read()},q.push_back(i);
solve(1,n,q);
for(int i=1;i<=Qm;i++) printf("%lld\n",ans[i]);
return 0;
}

浙公网安备 33010602011771号