cjy2003
2019-11-24 10:52:47
这是一个贪心题,首先说结论:最优情况是划分的最后一段最小。
我们来证明这个结论。
首先,设
考虑归纳证明:
长度为
假设对于长度
我们找到最大的
现在我们来说明对于其它满足
我们分别考虑以
首先,下面这种情况是不存在的。
在这种情况下,因为
于是最终一定是下面两种情况之一。
其中第一条红线为两种划分下的最后一个公共划分点(这里把
两种情况的区别仅仅在于公共划分点之后的第一个划分点属于前一种划分还是后一种划分。
然后除了第一种划分中最后的若干个划分点,其它的划分点都满足交替出现。
注意到我们只需要先说明
设前一种划分中公共划分点之后的每一段数字和为
现在我们不考虑原序列,只考虑这两个
我们来通过一些操作使
在两种情况中,如果
然后给
如果这时存在
这时,我们有
由于
如果有
否则有
于是,我们通过一些操作,使得
这样就证明完了。
具体实现可以用单调队列,需要手写高精度。
复杂度
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
using namespace std;
namespace io
{
const int N=1<<20;
char buf[N],*t1=buf,*t2=buf;
#define getc() t1==t2&&(t2=(t1=buf)+fread(buf,1,N,stdin),t1==t2)?EOF:*t1++
inline int read()
{
static int an;an=0;
static char ch;ch=getc();
while(ch<48||ch>57)ch=getc();
while(ch>=48&&ch<=57)an=(an<<3)+(an<<1)+(ch^48),ch=getc();
return an;
}
}
using io::read;
int n,op,f[40000010];
long long sum[40000010];
namespace task1
{
long long dp[51][50050];
void solve()
{
for(int i=1;i<=n;++i)
{
for(int j=0;j<=50000;++j)dp[i][j]=0x3f3f3f3f3f3f3f3fll;
for(int k=0;k<i;++k)dp[i][sum[i]-sum[k]]=dp[k][sum[i]-sum[k]]+(sum[i]-sum[k])*(sum[i]-sum[k]);
for(int j=1;j<=50000;++j)dp[i][j]=min(dp[i][j],dp[i][j-1]);
}
printf("%lld",dp[n][sum[n]]);
}
}
int x,y,z,m,p[100010],l[100010],r[100010],b[40000010],pos=1;
int rnd(int i)
{
if(i>2)b[i]=(b[i-1]*x+b[i-2]*y+z)&((1<<30)-1);
if(i>p[pos])++pos;
return b[i]%(r[pos]-l[pos]+1)+l[pos];
}
int q[40000010],h,t;
struct data
{
int a[4];
friend data operator * (data a,data b)
{
static long long C[4];
static data c;
memset(C,0,sizeof(C));
for(int i=0;i<4;++i)
for(int j=0;i+j<4;++j)
C[i+j]+=1ll*a.a[i]*b.a[j];
for(int i=0;i<4;++i)
if(C[i]>999999999)
{
C[i+1]+=C[i]/1000000000;
C[i]%=1000000000;
}
for(int i=0;i<4;++i)c.a[i]=C[i];
return c;
}
friend data operator + (data a,data b)
{
static long long C[4];
static data c;
memset(C,0,sizeof(C));
for(int i=0;i<4;++i)C[i]=a.a[i]+b.a[i];
for(int i=0;i<4;++i)
if(C[i]>1000000000)
{
C[i+1]+=C[i]/1000000000;
C[i]%=1000000000;
}
for(int i=0;i<4;++i)c.a[i]=C[i];
return c;
}
}cur,ans;
void print(data x)
{
static int st[100],tp;
for(int i=0;i<=3;++i)
{
int w=x.a[i];
for(int j=0;j<9;++j)st[++tp]=w%10,w/=10;
}
while(tp>1&&!st[tp])--tp;
while(tp)putchar(st[tp]^48),--tp;
}
int main()
{
// freopen("partition.in","r",stdin);
// freopen("partition.out","w",stdout);
n=read(),op=read();
if(!op)
{
for(int i=1;i<=n;++i)sum[i]=read();
}
else
{
x=read(),y=read(),z=read(),b[1]=read(),b[2]=read(),m=read();
for(int i=1;i<=m;++i)p[i]=read(),l[i]=read(),r[i]=read();
for(int i=1;i<=n;++i)sum[i]=rnd(i);
}
long long mx=0;
for(int i=1;i<=n;++i)mx=max(mx,sum[i]),sum[i]+=sum[i-1];
if(n<=50&&mx<=1000)
{
task1::solve();
return 0;
}
h=0,t=0;
for(int i=1;i<=n;++i)
{
while(h<t&&sum[q[h+1]]+sum[q[h+1]]-sum[f[q[h+1]]]<=sum[i])++h;
f[i]=q[h];
while(h<=t&&sum[q[t]]+sum[q[t]]-sum[f[q[t]]]>=sum[i]+sum[i]-sum[f[i]])--t;
q[++t]=i;
}
int x=n;
while(x)
{
cur.a[0]=(sum[x]-sum[f[x]])%1000000000;
cur.a[1]=(sum[x]-sum[f[x]])/1000000000;
cur.a[2]=cur.a[3]=0;
ans=ans+cur*cur;
x=f[x];
}
print(ans);
return 0;
}