• CSP 2019 Day2 T2 划分


    (f_{i,j})表示只考虑前(i)个数,最后一段的开始端点的前一个位置为(j)时的最优解
    那么有转移方程

    [f_{i,j}=(sum_i-sum_j)^2+min(f_{j,k}) 其中sum_j-sum_kle sum_i-sum_j ]

    其中(sum_i)表示前缀和
    显然直接转移是(O(n^3)) 可以获得(36)分的高分
    但是可以发现当(j)固定时,(i)向右移动满足条件最小的(k)一定不会向右移动,满足单调性
    只用维护(f_{j,k})的最小值就可以了
    时间复杂度为(O(n^2))可通过(64)
    观察这样一个东西:(盗别人的图,勿喷)

    假设(sumL+xle sumR)
    如果(x)放到(sumL)中,答案是(ans_1=(sumL+x)^2+sumR^2=sumL^2+sumR^2+x^2+2sumL*x)
    如果(x)放到(sumR)中,答案是(ans_2=(sumR+x)^2+sumL^2=sumL^2+sumR^2+x^2+2sumR*x)
    显然(sumLle sumR),因此(ans_1le ans_2)
    即如果都合法,那么最后一段的长度越小越好
    于是我们设(pre_i)表示只考虑前(i)个时,最优解时最后一段的开始端点的前一个位置(如果有多个位置则尽量靠后)
    若当前枚举到(i),考虑转移点(j)
    即要满足(sum_i-sum_jge sum_j-sum_{pre_j})
    也就是(sum_ige 2sum_j-sum_{pre_j})
    (val_j=2sum_j-sum_{pre_j}),那么(sum_ige val_j)
    我们要在满足这个限制的(j)中找到最大的(因为要让最后一段尽量小)
    这个东西显然可以用单调队列进行优化
    具体来说,每次求(pre_i)时,先把队首的所有过时决策剔除(如果下一个元素都满足(val_jle sum_i),那么就弹出队首,因为下一个一定比当前队首的更优)
    然后直接用队首来更新
    求出(pre_i)后加入队尾
    对于(prege pre_i),全部都要弹出(因为当前值比它小还比它靠后,那么它就没有机会更新其他答案了)
    最后再从(n)开始往回走统计答案即可

    time complexity:

    (O(n))

    code:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=4e7+5,mod=1<<30;
    typedef long long ll;
    int n,type,a[N],pre[N],q[N],l,r,x,y,z,b[N],m;
    ll s[N];
    inline ll dr(int x){return 2*s[x]-s[pre[x]];}
    inline int add(int x,int y){x+=y;return x>=mod?x-mod:x;}
    inline int mul(int x,int y){return 1ll*x*y%mod;}
    void write(__int128 x){if(x>9)write(x/10);putchar(x%10+'0');}
    int main()
    {
    	scanf("%d%d",&n,&type);
    	if(type)
    	{
    		scanf("%d%d%d%d%d%d",&x,&y,&z,b+1,b+2,&m);
    		for(int i=3;i<=n;++i)b[i]=add(z,add(mul(x,b[i-1]),mul(y,b[i-2])));
    		int la=0;
    		for(int i=1;i<=m;++i)
    		{
    			int p,l,r;scanf("%d%d%d",&p,&l,&r);
    			for(int j=la+1;j<=p;++j)a[j]=b[j]%(r-l+1)+l,s[j]=s[j-1]+1ll*a[j];
    			la=p;
    		}
    	}
    	else for(int i=1;i<=n;++i)scanf("%d",a+i),s[i]=s[i-1]+1ll*a[i];
    	l=1,r=0;
    	for(int i=1;i<=n;++i)
    	{
    		while(l+1<=r&&dr(q[l+1])<=s[i])++l;
    		if(dr(q[l])<=s[i])pre[i]=q[l];
    		while(l<=r&&dr(i)<=dr(q[r]))--r;
    		q[++r]=i;
    	}
    	int now=n;__int128 ans=0;
    	while(now)ans+=(__int128)(s[now]-s[pre[now]])*(s[now]-s[pre[now]]),now=pre[now];
    	write(ans);
    	return 0;
    }
    
  • 相关阅读:
    Spring编译AOP项目报错
    Intellij新建Spring项目引入用户目录下的Spring jar包
    python如何安装第三方库
    sizeof and strlen整理
    数轴上从左到右有n个点a[0],a[1]…,a[n-1],给定一根长度为L的绳子,求绳子最多能覆盖其中的几个点。要求算法复杂度为o(n)。
    百度笔试题--最长回文字串
    微软笔试题
    人群智商差不多,是什么引起了差距?
    低层次“努力学习”和学习的本质
    集成 solr6.5.1到 tomcat7(8) 中 (解决java.lang.NoSuchMethodError问题)
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/13919527.html
Copyright © 2020-2023  润新知