P5665 划分

我爱卡常,卡常爱我

题目大意

给你 (n) 个数,需要找到一些分界点 (1 leq k_1 lt k_2 lt cdots lt k_p lt n) ,使得

[sum_{i=1}^{k_1} a_i leq sum_{i=k_1+1}^{k_2} a_i leq cdots leq sum_{i=k_p+1}^{n} a_i ]

并最小化

[(sum_{i=1}^{k_1} a_i)^2 + (sum_{i=k_1+1}^{k_2} a_i)^2 + cdots + (sum_{i=k_p+1}^{n} a_i)^2 ]

题解

(part~1:O(n^3))

我们可以定义 (f_{i,j}) 表示到第 (i) 个数,同时通过第 (j) 的点转移过来的最小值,我们可以易得状态转移方程:

[egin{array}{c} f_{i,j}=min_{k=0}^{j-1}(f_{j,k}+(sum_i-sum_j)^2)\ s.t.~sum_j-sum_kle sum_i-sum_j end{array} ]

由于我们是需要枚举 (i,j,k) 的,所以复杂度是 (O(n^3)) 的。

预计得分:(36pts)

(Part~2:O(n^2))

我们可以发现,对于任意一个位置,最优解是满足最后一组最小的,所以我们可以记录对于每一个位置最后一组的转移点 (lst_i) ,那么转移方程就如下:

[egin{array}{c} f_i=min_{j=0}^{i-1}(f_j+(sum_i-sum_j)^2)\ s.t.~sum_j-sum_{lst_j}le sum_i-sum_j end{array} ]

预计得分:(64pts)

(Part~3:O(n))

对于上面 (dp) 方程的转移条件:

[sum_j-sum_{lst_j}le sum_i-sum_j ]

我们来移一下项:

[sum_ige 2sum_j-sum_{lst_j} ]

又因为 (sum) 数组是满足单调递增的,而且我们要找到最大的 (sum_j) 来使得 (sum_i-sum_j) 也就是最后一段最小,可以考虑单调队列优化,复杂度就为 (O(n)) 了。

预计得分:(88pts)

(Part~4:) 神仙卡常

观察数据范围,我们发现肯定是要写高精的,然后我们发现恶心的出题人,非常善意的卡了你的空间又卡了你的时间。

能开的符合题目条件的 (4 imes 10^7) 的数组你最多只能开两个,剩下的必须滚动或者直接一边算一边用。

等你解决了 (MLE) 后,你会发现,如果你写的是高精,你会被卡常。然后我就毅然决然的压了 (10^8) 的位,同时加上了火车头,爱了爱了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=4e7+5,M=1e5+5,MOD=(1<<30),EACH=1e9,LOG=5;
struct lll
{
	int s[LOG];
	int l;
	void print() const
	{
		if(l==0)
		{
			printf("0");
			return ;
		}
		for(int i=l;i>=1;--i)
		{
			printf("%lld",s[i]);
		}
		return ;
	}
	void operator = (const int &x)
	{
		memset(s,0,sizeof(s));
		l=0;
		int xx=x;
		while(xx>0)
		l++,xx/=EACH;
		xx=x;
		for(int i=1;i<=l;++i)
		s[i]=xx%EACH,xx/=EACH;
		return ;
	}
};
lll ans;
lll &operator + (const lll &a,const lll &x)
{
	ans=0;
	int jinwei=0;
	ans.l=max(a.l,x.l);
	for(int i=1;i<=ans.l;++i)
	{
		ans.s[i]=a.s[i]+x.s[i]+jinwei;
		jinwei=0;
		if(ans.s[i]>=EACH)
		{
			jinwei=ans.s[i]/EACH;
			ans.s[i]%=EACH;
			if(i==ans.l)
			++ans.l;
		}
	}
	return ans;
}
lll &operator * (const lll &a,const lll &x)
{
	ans=0;
	if(a.l==0||x.l==0)
	return ans;
	for(int i=1;i<=a.l;++i)
	{
		for(int j=1;j<=x.l;++j)
		{
			ans.s[i+j-1]+=a.s[i]*x.s[j];
		}
	}
	int jw=0;
	ans.l=a.l+x.l-1;
	for(int i=1;i<=ans.l;++i)
	{
		ans.s[i]+=jw;
		jw=0;
		if(ans.s[i]>=EACH)
		{
			jw=ans.s[i]/EACH;
			ans.s[i]%=EACH;
			if(i==ans.l)
			{
				++ans.l;
			}
		}
	}
	return ans;
}
int n,type;
int x,y,z,m;
int a,b[3];
int p,l,r;
int lst[N],sum[N];
int q[N],h=0,t=0;
lll res,data;
void special_read()
{
	scanf("%lld%lld%lld%lld%lld%lld",&x,&y,&z,&b[1],&b[2],&m);
	for(int i=1,j=1;i<=n&&j<=m;++i)
	{
		while(p<i) scanf("%lld%lld%lld",&p,&l,&r);
		if(i>=3)
		b[i%3]=((x*b[(i-1)%3]%MOD+y*b[(i-2)%3]%MOD)%MOD+z)%MOD;
		a=b[i%3]%(r-l+1)+l;
		sum[i]=sum[i-1]+a;
	}
	return ;
}
void normal_read()
{
	for(int i=1;i<=n;++i)
	scanf("%lld",&a),sum[i]=sum[i-1]+a;
	return ;
}
signed main()
{
	cin>>n>>type;
	if(type) special_read();
	else normal_read();
	q[h]=0;
	for(int i=1;i<=n;++i)
	{
		while(h<t&&sum[q[h+1]]*2-sum[lst[q[h+1]]]<=sum[i]) h++;
		lst[i]=q[h];
		while(h<t&&sum[q[t]]*2-sum[lst[q[t]]]>=sum[i]*2-sum[lst[i]]) t--;
		q[++t]=i;
		// for(int j=0;j<i;++j)
		// {
		// 	if(sum[i]-sum[j]<lst[j])
		// 	continue;
		// 	if(f[i]>f[j]+(sum[i]-sum[j])*(sum[i]-sum[j]))
		// 	f[i]=f[j]+(sum[i]-sum[j])*(sum[i]-sum[j]),
		// 	lst[i]=sum[i]-sum[j];
		// }
	}
	int tmp=n;
	res=0;
	while(tmp)
	{
		data=sum[tmp]-sum[lst[tmp]];
		data=data*data;
		res=res+data;
		tmp=lst[tmp];
	}
	res.print(),printf("
");
	return 0;
}
原文地址:https://www.cnblogs.com/Point-King/p/13532994.html