专栏文章

P11953 [科大国创杯初中组 2023] 石子 题解

P11953题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mipfnkw7
此快照首次捕获于
2025/12/03 11:14
3 个月前
此快照最后确认于
2025/12/03 11:14
3 个月前
查看原文
O(n2)O(n^2) 的区间 DP 是显然的,但似乎是场上的最高分。
DP 是没有前途的,我们贪心地考虑该问题。
设起点为 xx,先考虑一种特殊情况,即 xx 左边单调递减,右边单调递增。
此时显然可以直接贪心选较小的,很好做,故考虑将原问题转化至此情况。
将一个区间的点抽象成块,考虑调整左右两个块的顺序会如何。
Δans=len2S1len1S20\Delta ans=len_2S_1-len_1S_2\le 0,即 a1=S1len1S2len2=a2\overline{a_1}=\frac{S_1}{len_1}\le\frac{S_2}{len_2}=\overline{a_2}
即一般地,先处理平均值较小的块。
所以,以 ii 启的向右的块一定延申至 jj,使得 a\overline{a} 最小(向左同理),计算的时间复杂度为均摊 O(n)O(n)
这样,我们就有了一个单点 O(n)O(n) 的做法。
由于数据随机,所以做法其实是单点 O(logn)O(\log n),总 O(nlogn)O(n\log n) 的。
如果不随机,我们发现答案只与 xx 两边块的状态(如 lenlenSSiai\sum ia_i,etc. )与它们平均值的顺序有关,离散化后扔进 DS (如线段树)里即可,移动时修改。
贴一下保证随机的代码(目前最优解):
CPP
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100005;
int a[N],f[N],g[N],n,l,r;
ll s[N],s2[N];
ll S(const int l,const int r)
{
	return s[r]-s[l-1];
}
ll cost(const int x,const int dir,const int t)
{
	int r=dir?g[x]:f[x];
	if(dir==1)
		return (t+x)*S(x,r)-(s2[r]-s2[x-1]);
	else
		return (t-x)*S(r,x)+s2[x]-s2[r-1];
}
void init()
{
	int x;
	f[1]=1;
	for(int i=2;i<=n;++i)
	{
		f[i]=i,x=i-1;
		while(x>0&&(i-f[i]+1)*S(f[x],x)<=(x+1-f[x])*S(f[i],i))
			f[i]=f[x],x=f[x]-1;
	}
	g[n]=n;
	for(int i=n-1;i>=1;--i)
	{
		g[i]=i,x=i+1;
		while(x<=n&&(g[i]-i+1)*S(x,g[x])<=(g[x]-x+1)*S(i,g[i]))
			g[i]=g[x],x=g[x]+1;
	}
}
void solve(const int s)
{
	int l=s-1,r=s+1,t=n-1;
	long long ans=(ll)(n-1)*a[s];
	while(l>0&&r<=n)
	{
		if(S(f[l],l)*(g[r]-r+1)<=S(r,g[r])*(l-f[l]+1))
			ans+=cost(l,0,t),t-=(l-f[l]+1),l=f[l]-1;
		else
			ans+=cost(r,1,t),t-=(g[r]-r+1),r=g[r]+1;
	}
	while(l>0)
		ans+=cost(l,0,t),t-=(l-f[l]+1),l=f[l]-1;
	while(r<=n)
		ans+=cost(r,1,t),t-=(g[r]-r+1),r=g[r]+1;
	cout<<ans<<' ';
}
int main()
{
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>l>>r;
	for(int i=1;i<=n;++i)
		cin>>a[i],s[i]=s[i-1]+a[i],s2[i]=s2[i-1]+1LL*a[i]*i;
	init();
	for(int i=l;i<=r;++i)
		solve(i);
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...