UNR#6 稳健型选手【题解】
题意:有 \(n\) 个数 \(a_{1...n}\),有 \(q\) 次询问,每次输入 \(l,r\),表示把 \(a_l,a_{l+1},...,a_r\) 排成一排,然后两个人 \(\text{A,B}\) 进行取数游戏:
-
\(\text A\) 先手,并且绝顶聪明。
-
\(\text B\) 只会取当前存在的最左边的数。
求最终 \(\text A\) 能取得的数的和的最大值。
\(1\le n,q\le 2\times 10^5\)
观察性质。不难发现,对于一个数字序列 \(b_1,b_2,...,b_m\),其任意一个长度为 \(i\) 前缀,\(\text A\) 取的数个数不超过 \(\lceil \frac i2 \rceil\),那么合法。
考虑对于单个询问怎么做,可以反悔贪心:
每次加入一个数,已加入的数个数为奇数时,令当前加入的数为 \(\text A\) 取的数;为偶数时,考虑从 \(\text A\) 取的数中选一个最小的和当前比较,进行反悔。
有多组询问,考虑猫树分治。
设当前 \(mid\),处理的询问区间为 \([l_i,r_i]\)。为了减小码量,统一令区间分出来的左半边的长度为偶数,可以枚举按 \(mid/mid+1\) 来划分。
设以 \([l_i,p],[p+1,r_i](p\in\{mid,mid+1\})\) 划分,那么我们需要算出左边和右边分别进行取数的结果。合并两边,可以考虑左边取的数值可重集合 \(S_1\),右边未取的数值可重集合 \(S_2\),不难发现 \(\text{merge}(S_1,S_2)\) 中前 \(|S_1|\) 大的数字和为所求。
维护集合可以用主席树,时间复杂度 \(O(n\log^2n)\)。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=2e5+10;
ll n,m,a[maxn],cnt,rt[maxn*2],lc[maxn*60],rc[maxn*60],sum[maxn*60],sc[maxn*60],ans[maxn],h[maxn],ht,pre[maxn];
struct query
{
ll l,r,id;
};
vector<query>b;
priority_queue<ll>q;
void clr()
{
while(!q.empty()) q.pop();
}
void modify(ll &p,ll q,ll l,ll r,ll x)
{
p=++cnt; lc[p]=rc[p]=sum[p]=sc[p]=0;
if(l==r)
{
sc[p]=sc[q]+1;
sum[p]=sc[p]*h[x];
return;
}
lc[p]=lc[q]; rc[p]=rc[q];
ll mid=l+r>>1;
if(x<=mid) modify(lc[p],lc[q],l,mid,x);
else modify(rc[p],rc[q],mid+1,r,x);
sc[p]=sc[lc[p]]+sc[rc[p]];
sum[p]=sum[lc[p]]+sum[rc[p]];
}
ll ask(ll p,ll q,ll l,ll r,ll k)
{
if(l==r) return h[l]*k;
ll mid=l+r>>1;
if(sc[rc[p]]+sc[rc[q]]>=k) return ask(rc[p],rc[q],mid+1,r,k);
return ask(lc[p],lc[q],l,mid,k-sc[rc[p]]-sc[rc[q]])+sum[rc[p]]+sum[rc[q]];
}
void solve(ll l,ll r,vector<query>b)
{
if(b.empty()) return;
if(l==r)
{
for(ll i=0;i<b.size();i++) ans[b[i].id]=h[a[l]];
return;
}
vector<query>b1,b2;
ll mid=l+r>>1;
for(ll i=0;i<b.size();i++)
if(b[i].r<=mid) b1.push_back(b[i]);
else if(b[i].l>mid) b2.push_back(b[i]);
for(ll o=0;o<2;o++)
{
ll p=((mid&1)==o? mid:mid+1);
clr();
cnt=0; for(ll i=l-1;i<=r+1;i++) rt[i]=0;
for(ll i=p;i>=l;i--)
{
rt[i]=rt[i+1];
q.push(a[i]);
if((p-i)&1)
{
ll tmp=q.top(); q.pop();
modify(rt[i],rt[i],1,ht,tmp);
}
}
clr();
for(ll i=p+1;i<=r;i++)
{
rt[i]=(i==p+1? 0:rt[i-1]);
if((i-p)&1)
{
q.push(-a[i]);
}
else
{
ll tmp=-q.top();
if(tmp<a[i])
{
modify(rt[i],rt[i],1,ht,tmp);
q.pop(); q.push(-a[i]);
}
else
{
modify(rt[i],rt[i],1,ht,a[i]);
}
}
}
for(ll i=0;i<b.size();i++)
if(b[i].l<=mid&&mid<b[i].r&&(b[i].l&1)==(o^1))
{
if(b[i].r!=p)
{
ll res=pre[b[i].r]-pre[p]-sum[rt[b[i].r]];
res+=ask(rt[b[i].l],rt[b[i].r],1,ht,(p-b[i].l+1)/2);
ans[b[i].id]=res;
}
else ans[b[i].id]=sum[rt[b[i].l]];
}
}
solve(l,mid,b1);
solve(mid+1,r,b2);
}
int main()
{
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++) scanf("%lld",a+i), h[++ht]=a[i], pre[i]=pre[i-1]+a[i];
sort(h+1,h+1+ht);
ht=unique(h+1,h+1+ht)-h-1;
for(ll i=1;i<=n;i++) a[i]=lower_bound(h+1,h+1+ht,a[i])-h;
b.resize(m);
for(ll i=0;i<m;i++) scanf("%lld%lld",&b[i].l,&b[i].r), b[i].id=i;
solve(1,n,b);
for(ll i=0;i<m;i++) printf("%lld\n",ans[i]);
return 0;
}