[CSP day2T2]划分

划分

题解

本题很明显的一道dp题。

我们可以先写出一个朴素的dp,暴力就不分析了dp_{i,j}表示将j+1-i的划为一段的最小值。很明显,这是一个O\left ( n^{3} \right )的算法,只能得到32分。我们需要考虑一下dp的优化,一眼单调队列(笔者也只想得到这个)。我们将划分点定下来,先将所有在它前面的塞入队列,再更新再划分点后面的点,就可以做到O\left ( n^{2} \right ),有64分了。

之后是一个十分重要的结论:

dp_{i,j}\leq dp_{i,j-1},这其实通过单调队列分析得到。

接下来是证明,请别怪笔者能力有限,复制证明,笔者也没证出来

引用自:日志 - matthew99的博客

结论

设最优解的倒数第k段的开始位置为i,那么对于所有的满足段和不递减条件的解,一定有下列条件之一:

  1. 该解不存在k段。
  2. 该解从右到左数第k段的开始位置至少为i

这里我们假设最优解的倒数最后一段(也就是第一段)的开始位置是1,所以上述条件蕴含没有任何解的段数比最优解要多。

换句话说,如果你把所有解的断点从大到小写下来,然后剩下的位置补0,那么最优解对应的序列在所有位置都是最大值。

同时容易注意到满足这个结论中条件的解一定唯一,因此最优解释良定义的。

根据结论推出的线性做法

设pipi表示以ii结尾的前缀,最后一段的位置的最大值,那么pipi一定是满足

\sum _{k=p_{j}}^{j-1}a_{k}\leq \sum _{k=j}^{i}a_{k}的最大的j

这个非常显然,因为p_{i}对应的是每个位置结尾的前缀最后一段的最小和,如果不存在k> j使得上述条件满足,那么j一定是最后一段位置的开头的位置的最大值。

通过记录前缀和,这个东西很好用单调队列线性维护。

最后答案就是按照p不断往前走。

容易证明到p满足不递减性,所以按照p不断走得到的解一定是满足结论中条件的解。

结论的证明

对于每个解,我们可以从后往前将每一段的和写出来,然后补无限个零,得到一个对应的序列。

从结论我们容易推出,满足结论中条件的解对应的序列的任何位置的前缀和一定是所有解对应位置的前缀和中最大的。

现在,我们抛弃原序列,只考虑这个和构成的序列,假设满足结论中条件的解对应的序列是b_{i},我们现在找到另外一个解,它的序列是c_{i},且满足:

\forall k,\sum _{i\leq k}b_{i}\leq \sum _{i\leq k}c_{i}

我们需要证明:

\sum b_{i}^{2}\leq \sum c_{i}^{2}

我们注意到对于一个单调不递增的序列x,如果我们选出两个下标i< j,使得x_{i}\geq x_{j}+2,并将x_{i}减一,x_{j}加一,那么操作之后x依然单调不递增,且么这个操作会使x的平方和减少。

证明的思路是,证明可以通过一些合法的移动将c变为b,且任意时刻c的所有位置前缀和仍然大于b的对应位置的前缀和。这样就可以证明c的平方和一定不会小于b的平方和。

我们只要证明对于任意不同于bc,可以找到一个合法的,保持前缀和性质的移动,因为一次移动之后可以使平方和变小,证明的剩下部分很好使用按照平方和的数学归纳法解决。

我们找到第一个c的前缀和大于b的前缀和的位置,因为c的前缀和不会小于b,如果不存在这样的位置,那么只可能cb相同,这与bc不同的假设矛盾。

假设这个位置为u,我们找到第一个v> u使得v这个位置两个序列对应的前缀和相等,因为bc的总和相等且我们补了零,容易发现这个位置一定能找到。

 考虑c_{u}c_{v}。如果你写下c_{i}-b_{i}的值,在这个区间内这个值的和为0,且c_{u}-b_{u}> 0(因为u是第一个c的前缀和大于b的前缀和的位置),那么一定有另一个位置c_{i}-b_{i}< 0,由于b_{i}单调不递减,肯定有这个位置的c_{i}\leq c_{u}-2,这意味着这个区间中c的权值的跨度至少为2。

那么我们找到最小的c_{x}\neq c_{u}x,最大的c_{y}\neq c_{v}y,设i= x+1,j= y-1容易发现这是一个合法的移动,且保持了前缀和性质。

于是任何一个解对应的序列都可以通过若干次移动得到满足结论中条件的解对应的解,这就证明了满足结论中条件的解的平方和是最小的,是最优解。

以上就是证明,所以,我们可以在记录下每个dp的最大值与其划分的位置,很容易就能达到O\left ( n^{2} \right ),用单调队列维护就可以达到O\left ( n \right ),这样就有了88分。之后就是最坑的type= 1的情况,需要高精进行处理,其实出题者也就想卡掉O\left ( nlog_{n} \right )而已,顺便增加一下码量,好像有许多巨佬考场上都未去拿这12分。一个压位高精,不过要尽量压一下,否则会被卡常。好像有人用__int128卡过了。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXM 100005 
#define MAXN 40000005
using namespace std;
typedef long long LL;
const LL mo=(1<<30);
const LL W=1000000;
LL n,type,sum[MAXN];
LL nxt[MAXN],dp[MAXN];
LL q[MAXN],head,tail;
LL ans[5],len;
template<typename _T>
inline void read(_T &x)
{
    _T f=1;x=0;char s=getchar();
    while(s>'9'||s<'0'){if(s=='-') f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
    x*=f;
}
inline void gjf(LL x)
{
	LL s=x,b[5]={},lenb=0;
	while(s) b[lenb++]=s%W,s/=W;
	s=0;
	for(int i=0;i<lenb||s;i++)
	{
		LL k=(i<lenb?b[i]:0)*x+s;
		//printf("%lld %lld %lld\n",x,i,k);
		s=k/W;ans[i]+=k%W;
		if(i>=len) len++;
	}
	for(int i=0;i<len||s;i++)
	{
		LL k=ans[i]+s;
		s=k/W;ans[i]=k%W;
		if(i>=len) len++;
	}
}
int main()
{
	read(n);read(type);
	LL x,y,z,m,b1,b2;
	if(type==1)
	{
		read(x);read(y);read(z);
		read(b1);read(b2);read(m);
	}
	LL p=0,l=0,r=0;
	head=tail=1;q[tail]=0;
	for(int i=1;i<=n;i++)
	{
		if(type==0)
		{
			LL x;read(x);
			sum[i]=sum[i-1]+x;
		}
		else
		{
			LL a,b;
			if(p<i)
				read(p),read(l),read(r);
			if(i>2)
			{
				b=(x*b2%mo+y*b1%mo+z)%mo;
				b1=b2;b2=b;
			}
			if(i==1) b=b1;
			if(i==2) b=b2;
			a=b%(r-l+1)+l;
			sum[i]=sum[i-1]+a;
		}
		while(head<tail&&sum[i]-sum[q[head+1]]>=dp[q[head+1]]) head++;
		dp[i]=sum[i]-sum[q[head]];nxt[i]=q[head];
		while(head<tail&&dp[q[tail]]-sum[i]+sum[q[tail]]>=dp[i]) tail--;
		q[++tail]=i;
	}
	for(int i=n;i>0;i=nxt[i])
	{
		LL xs=sum[i]-sum[nxt[i]];
		gjf(xs);
	}
	printf("%lld",ans[len-1]);
	for(int i=len-2;~i;i--)
		printf("%06lld",ans[i]);
    return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值