[CSP-S2019]划分 题解

CSP-S2 2019 D2T2

考场上读完题感觉是DP就直接扔了开T3了,考完才发现部分分好像不难拿,枯了


题意分析

给出一个数列,要求将其分成几段,使每段的和非严格递增,求最小的每段的和的平方和。

思路分析

可以发现,分成的段越多,即合并越少,答案越小;因此我们希望每段的和都尽量小。这提供了一个贪心的思想来解题。

以上只是合情推理,大家看着开心就好,不过看起来似乎没什么问题

fi表示数列[a1,ai]最后一段的和最小时最后一个断点(若需要整段合并,则为0)。这个fi需要在使分段满足题目要求的单调性的前提下,尽量地大,也就是使最后一段的和尽量小,从而使每段的和都尽量小。因此,对于每个i,我们可以找到最大的合法的fi。每次都遍历一遍暴力查找,复杂度O(n2),预计可以得到64分的高分。

查找时用数列前缀和来求段和,设sumi表示i位置的前缀和。

通过分析或者手玩样例可以发现,fi一定是单调不降的,并且判定合法的条件sumisumjsumjsumfj,可以变形为sumisumj+(sumjsumfj),根据题意,sumjsumfjsumj都具有单调性。显然,这可以用单调队列来优化,复杂度O(n)

据说,为了防止被看出可以直接推出分段然后求解答案,出题人放弃了取模而使用了高精。然而我太懒,本文的代码直接用int128实现。当然,考场上是用不了int128的,还是要熟练高精才行。

注意要尽量优化空间,不需要存储的信息可以不用存储,否则会导致正解MLE的惨状。

复制代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define INT __int128
using namespace std;
const int N=4e7+100,P=1073741824;
int n,type,head=1,tail=1,x,y,z,m,p,l,r;
int f[N],q[N];
ll b1,b2,b3,w;
ll sum[N];
INT ans;
void in1()
{
    scanf("%d%d%d%lld%lld%d",&x,&y,&z,&b1,&b2,&m);
    int now=1;ll w;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&p,&l,&r);
        while(now<=p)
        {
            if(now==1)
                w=(b1%(r-l+1))+l,sum[1]=w;
            else
                if(now==2)
                    w=(b2%(r-l+1))+l,sum[2]=sum[1]+w;
                else
                {
                    b3=(x*b2+y*b1+z)%P;
                    w=(b3%(r-l+1))+l,sum[now]=sum[now-1]+w;
                    b1=b2,b2=b3;
                }
            now++;
        }
    }
}//特殊输入
void write(INT x)
{
    if(x>9) 
        write(x/10);
    putchar(x%10+'0');
}//int128不能直接输出
int main()
{
    scanf("%d%d",&n,&type);
    if(type)
        in1();
    else
        for(int i=1;i<=n;i++)
            scanf("%lld",&w),sum[i]=sum[i-1]+w;
    for(int i=1;i<=n;i++)
    {
        while(head<tail && 2*sum[q[head+1]]-sum[f[q[head+1]]]<=sum[i])
            head++;//找到最大的f[i]
        f[i]=q[head];
        while(head<tail && 2*sum[q[tail]]-sum[f[q[tail]]]>=2*sum[i]-sum[q[head]])
            tail--;//维护单调性
        q[++tail]=i;
    }
    int now=n;
    while(now)
    {
        ans+=(INT)(sum[now]-sum[f[now]])*(sum[now]-sum[f[now]]);
        now=f[now];
    }//计算答案
    write(ans);
    return 0;
}
复制代码
posted on   TEoS  阅读(777)  评论(1编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· DeepSeek火爆全网,官网宕机?本地部署一个随便玩「LLM探索」
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 上周热点回顾(1.20-1.26)
· 【译】.NET 升级助手现在支持升级到集中式包管理
< 2025年1月 >
29 30 31 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 1
2 3 4 5 6 7 8

点击右上角即可分享
微信分享提示