题解:P13523 [KOI 2025 #2] 序列与查询

题目:

长度相同的子段受 \(x\) 影响相同。

哇好厉害的性质,可以直接把每个长度的最大子段和跑下来,询问 \(X\) 相当于找 \(val_{len}+len×X\) 最大的 \(len\),预处理 \(O(n^2+qn)\),查询优化?

考虑画到坐标轴上,把 \((len,val_{len})\) 看成一个点,画一个 \(y=Xx\) 的直线,发现每个点的答案其实是纵坐标加上横坐标在 \(y=kx\) 的值。

凭借小学数学,敏锐地再画一个 \(y=-Xx\) 的直线,这样每个点的答案是每个点向下到直线的距离,已知直线去选最远的点,这很像凸包,简单思考一下确实是。复杂度 \(O(n^2+q\log n)\)

思考如何优化预处理的过程,序列分治上做肯定简单点,再思考怎么拼接左面的后缀和右面的前缀。

大概长这样:
\(dp_{i+j}=hz_i+qz_j\)
其中:
\(dp_i\)\(i\) 长度的最大子段和。
\(hz_i\)\([mid-i+1,mid]\) 的和。
\(qz_j\)\([mid+1,mid+1+j-1]\) 的和。

闵可夫斯基和优化,把 \(hz\)\(qz\) 的凸包维护出来,然后合并后更新 \(dp\),复杂度 \(O(n\log n+q\log n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int read()
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*f;
}
const int QAQ=1e6+7,inf=1e17+7;
int n,q,a[QAQ],x[QAQ],d[QAQ],cnt,o1,o2,o3;
struct xxx {int x,y;} tb[QAQ],zb[QAQ],yb[QAQ],he[QAQ];
double xie(xxx a,xxx b) {return 1.0*(b.y-a.y)/(b.x-a.x);}
xxx operator +(xxx a,xxx b) {return {a.x+b.x,a.y+b.y};}
xxx operator -(xxx a,xxx b) {return {a.x-b.x,a.y-b.y};}
void chuli(int l,int r)
{
	if(l==r) return d[1]=max(d[1],a[l]),void();
	int mid=(l+r)>>1;
	chuli(l,mid),chuli(mid+1,r);
	o1=o2=0;
	int sum=0;
	for(int i=mid;i>=l;i--)
	{
		sum+=a[i];
		xxx nw={mid-i+1,sum};
		while(o1&&xie(zb[o1-1],zb[o1])<xie(zb[o1-1],nw)) o1--;
		zb[++o1]=nw;
	}
	sum=0;
	for(int i=mid+1;i<=r;i++)
	{
		sum+=a[i];
		xxx nw={i-(mid+1)+1,sum};
		while(o2&&xie(yb[o2-1],yb[o2])<xie(yb[o2-1],nw)) o2--;
		yb[++o2]=nw;
	}
	if(!o1)
	{
		for(int i=1;i<=o2;i++) d[yb[i].x]=max(d[yb[i].x],yb[i].y);
		return ;
	}
	if(!o2)
	{
		for(int i=1;i<=o1;i++) d[zb[i].x]=max(d[zb[i].x],zb[i].y);
		return ;
	}
	o3=0;
	he[++o3]=zb[1]+yb[1];
	for(int i=o1;i>1;i--) zb[i]=zb[i]-zb[i-1];
	for(int i=o2;i>1;i--) yb[i]=yb[i]-yb[i-1];
	int ccx=2,swl=2;
	while(ccx<=o1&&swl<=o2)
	{
		if(atan2(1.0*zb[ccx].y,1.0*zb[ccx].x)>=atan2(1.0*yb[swl].y,1.0*yb[swl].x))
			he[++o3]=zb[ccx],ccx++;
		else he[++o3]=yb[swl],swl++;
	}
	while(ccx<=o1) he[++o3]=zb[ccx],ccx++;
	while(swl<=o2) he[++o3]=yb[swl],swl++;
	for(int i=2;i<=o3;i++) he[i]=he[i]+he[i-1];
	for(int i=1;i<=o3;i++) d[he[i].x]=max(d[he[i].x],he[i].y);
}
signed main()
{
	cin>>n>>q;
	for(int i=1;i<=n;i++) a[i]=read(),d[i]=-inf;
	chuli(1,n);
	tb[0]={0,-inf};
	for(int i=1;i<=n;i++)
	{
		xxx nw={i,d[i]};
		while(cnt&&xie(tb[cnt-1],tb[cnt])<xie(tb[cnt-1],nw)) cnt--;
		tb[++cnt]=nw;
	}
	for(int i=1,x;i<=q;i++)
	{
		x=read();
		int l=1,r=cnt,mid,da;
		double k=-x;
		while(l<r)
		{ 
			mid=(l+r)>>1;
			if(xie(tb[mid],tb[mid-1])<k) r=mid;
			else l=mid+1,da=mid;
		}
		if(xie(tb[r],tb[r-1])>=k) da=r;
		printf("%lld\n",tb[da].y+tb[da].x*x);
	}
	return 0;
}
posted @ 2025-09-26 23:01  _a1a2a3a4a5  阅读(9)  评论(0)    收藏  举报