Loading

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;
}
posted @ 2023-10-05 16:19  Lgx_Q  阅读(16)  评论(0编辑  收藏  举报