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\) 的点转移过来的最小值,我们可以易得状态转移方程:

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

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

预计得分:\(36pts\)

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

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

\[\begin{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_i\ge 2sum_j-sum_{lst_j} \]

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

预计得分:\(88pts\)

\(Part~4:\) 神仙卡常

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

能开的符合题目条件的 \(4\times 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("\n");
	return 0;
}
posted @ 2020-08-20 08:25  Point_King  阅读(175)  评论(0编辑  收藏  举报