CSP-S 2022 T2 题解

简述题意

有数列 \(a_1,a_2\ldots a_n\)\(b_1,b_2\ldots b_m\),每次给出区间 \([l_1,r_1]\)\([l_2,r_2]\),甲选择 \(x\in [l_1,r_1]\cap\mathbb{Z}\),乙选择 \(y\in[l_2,r_2]\cap\mathbb{Z}\),最后得分为 \(a_xb_y\),甲希望得分最大,乙希望得分最小,两人都足够聪明,\(q\) 次询问,每次给出 \([l_1,r_1],[l_2,r_2]\),问最终得分。

数据范围:\(1\le n,m,q\le 10^5,-10^9\le a_i,b_i\le 10^9\),所有数都是整数。

解题思路

可以假设是甲先选一个数,乙再选一个数使得甲选的数变小。假设甲选的数为 \(x\) 时乙的最优决策为 \(f(x)\),那么最后的答案就是 \(\max\{xf(x)\}\)

不难发现乙可能的最优决策只有 \(O(1)\) 种,乙只能选择最小/大的非负/负数。

考虑证明:

在所有乙可以选择的数中,设最小的非负数为 \(b_{\min}\),最大的非负数为 \(b_{\max}\)绝对值最小的负数为 \(-b_{\min}\)绝对值最大的负数为 \(-b_{\max}\)。再设甲选择的数为 \(x\)

情况一,\(x=0\)

这时乙的选择并不重要,可以直接钦定乙选择 \(\{b_{\min},b_{\max},-b_{\min},-b_{\max}\}\) 中的一个,这不影响我们结论的正确性。

情况二,\(x>0\)

如果乙可以选择负数,那么乙一定会选择 \(-b_{\max}\),否则乙一定选择 \(b_{\min}\)

假如存在 \(xy<x(-b_{\max})\),结合 \(x>0\) 的假设可知 \(y<-b_{\max}\),与假设矛盾。选择 \(b_{\min}\) 时的证明同理。

情况三,\(x<0\)

如果乙可以选择正数,那么乙一定会选择 \(b_{\max}\),否则乙一定选择 \(-b_{\min}\)

仿照情况二的证明即可。

综上可知结论成立。

又发现甲乙的操作顺序并不重要,我们可以看做乙先选一个数,甲再选一个数使得得分最大。

交换操作顺序后的情况和交换之前是对称的,同样我们可以有:甲只有 \(O(1)\) 种可能的最优决策,只能选择最小/大的非负/负数。

于是只需要枚举甲的决策,判断乙对应的最优决策即可。

现在需要求出 \(b_{\min},b_{\max},-b_{\min},-b_{\max}\) 。这就是稍微变种的 RMQ 问题,我们考虑把非负数和负数分离开,分别用线段树或者 ST 表维护区间最值即可。

算法流程
  1. 用数据结构分别维护 \(a,b\) 上的区间最大/小的非负/负数。
  2. 对于每次询问,枚举甲可能的最优决策,判断乙对应的最优决策。

时间复杂度 \(O(n\log n+q)\)\(O(n\log n+q\log n)\)

代码

维护方便,我写了 \(8\) 颗线段树,这样就只有 \(2\) 种 pushup 的规则,数据结构部分比较好写,不过常数大了不少。

给出主要部分的代码:

const int N=1e5+5,M=2e6+5;
int n,m,q,a[N],b[N];
class node
{
	public:
		int c[2];
		int val;
}; node s[M]; int cnt;
#define ls(x) s[x].c[0]
#define rs(x) s[x].c[1]
#define mid ((l+r)>>1)
#define L(x) ls(x),l,mid
#define R(x) rs(x),mid+1,r
void build(int &x,int l,int r)
{
	if(!x) x=++cnt;
	if(l==r) return;
	build(L(x)),build(R(x));
}
inline void pushup(int x,bool tp)
{
	if(tp) s[x].val=max(s[ls(x)].val,s[rs(x)].val);
	else s[x].val=min(s[ls(x)].val,s[rs(x)].val);
}
void insert(int x,int l,int r,int p,int k,bool tp)
{
	if(l==r&&l==p) {
		s[x].val=k;
		return;
	}
	if(p<=mid) insert(L(x),p,k,tp);
	else       insert(R(x),p,k,tp);
	pushup(x,tp);
}
inline int merge(int x,int y,bool tp)
{
	if(tp) return max(x,y);
	else return min(x,y);
}
const int inf=2e9;
int query(int x,int l,int r,int ql,int qr,bool tp)
{
	if(!x||l>qr||r<ql) return tp?-inf:inf;
	if(l>=ql&&r<=qr) return s[x].val;
	return merge(query(L(x),ql,qr,tp),query(R(x),ql,qr,tp),tp);
}
class segtree
{
	public:
		int rt,L,R;
		bool mx;
		inline void make(int l,int r) { L=l,R=r; build(rt,L,R); }
		inline int ask(int l,int r) { return query(rt,L,R,l,r,mx); }
		inline void ins(int p,int k) { insert(rt,L,R,p,k,mx); }
};
segtree amx,amn,aamx,aamn;
segtree bmx,bmn,bbmx,bbmn;
//依次维护最大非负数,最小非负数,绝对值最小负数,绝对值最大负数

inline void work()
{
	int l1,r1,l2,r2;
	ll as;
	for(int i=1,ax,bx,an,bn,aax,bbx,aan,bbn;i<=q;++i)
	{
		as=LLONG_MIN;
		read_(l1),read_(r1),read_(l2),read_(r2);
		ax=amx.ask(l1,r1);
		aax=aamx.ask(l1,r1);
		an=amn.ask(l1,r1);
		aan=aamn.ask(l1,r1);
		bx=bmx.ask(l2,r2);
		bbx=bbmx.ask(l2,r2);
		bn=bmn.ask(l2,r2);
		bbn=bbmn.ask(l2,r2);
		if(ax!=-inf)
		{
			if(bbn!=inf) as=max(as,1ll*ax*bbn);
			else as=max(as,1ll*ax*bn);
		}
		if(aax!=-inf)
		{
			if(bx!=-inf) as=max(as,1ll*aax*bx);
			else as=max(as,1ll*aax*bbx);
		}
		if(an!=inf)
		{
			if(bbn!=inf) as=max(as,1ll*an*bbn);
			else as=max(as,1ll*an*bn);
		}
		if(aan!=inf)
		{
			if(bx!=-inf) as=max(as,1ll*aan*bx);
			else as=max(as,1ll*aan*bbx);
		}
		cout<<as<<'\n';
	}
}

void init()
{
	amx.make(1,n),amn.make(1,n);
	amx.mx=1,amn.mx=0;
	bmx.make(1,m),bmn.make(1,m);
	bmx.mx=1,bmn.mx=0;
	aamx.make(1,n),aamn.make(1,n);
	aamx.mx=1,aamn.mx=0;
	bbmx.make(1,m),bbmn.make(1,m);
	bbmx.mx=1,bbmn.mx=0;
	for(int i=1;i<=n;++i)
	{
		if(a[i]>=0) amx.ins(i,a[i]),amn.ins(i,a[i]),aamx.ins(i,-inf),aamn.ins(i,inf);
		else        amx.ins(i,-inf),amn.ins(i,inf),aamx.ins(i,a[i]),aamn.ins(i,a[i]);
	}
	for(int i=1;i<=m;++i)
	{
		if(b[i]>=0) bmx.ins(i,b[i]),bmn.ins(i,b[i]),bbmx.ins(i,-inf),bbmn.ins(i,inf);
		else        bmx.ins(i,-inf),bmn.ins(i,inf),bbmx.ins(i,b[i]),bbmn.ins(i,b[i]);
	}
}
void solmain()
{
	read_(n),read_(m),read_(q);
	for(int i=1;i<=n;++i) read_(a[i]);
	for(int i=1;i<=m;++i) read_(b[i]);
	init();
	work();
}

这题还是比较细的,考场上要耐心打一打。

posted @ 2022-10-31 17:44  嘉年华_efX  阅读(45)  评论(0编辑  收藏  举报