1 条题解

  • 0
    @ 2025-8-24 22:14:16

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Su_Zipei
    与卿再世相逢日,玉树临风一少年

    搬运于2025-08-24 22:14:16,当前版本为作者最后更新于2020-04-14 07:55:17,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    _ _ _ _# 原题链接:https://www.luogu.com.cn/problem/P5774

    分析

    直接看这道题,第一个困惑点,那个绝对值的比较是什么东西,根据数学知识,我们可以知道这个意思是kkii的距离小于kkjj的距离,而路线是线性的,这就意味着当且仅当kkjj的左边时才成立,不然总会有ki>kjk-i>k-j,还不理解?看下图 如果KKKK'的位置,那么KiK-i一定大于KjK-j吧,所以这个题的题意是只要从jj往回走去治愈KK,就必须把之前没治愈过的村庄也治愈了。

    想到这里,状态就差不多出来了,定义DPiDP_i表示治愈前i个村庄的最小死亡数,下面考虑状态转移,对于JYYJYY来说,每个村庄它都有两个选择,治愈oror先去别的再走回来治愈,治愈的话很好弄,主要考虑的就是略过它的情况,这时候如果依次枚举KK,效率应该是N3N^3,程序吃不消,30003000的极限数据我们最少也要压到N2N^2左右,所以接下来考虑优化。

    优化其实也挺简单的,主要有一点很恶心,下边再说。(从这里开始默认jjii的前边,请勿被上图迷惑)我们发现多出来的时间主要是用在了计算略过村庄再回来的死亡人数的计算,所以我们可以先考虑预处理出从jjii再从iijj然后又回到i这一过程中最少的死亡数,于是定义gi,jg_{i,j}含义为上述的来辅助我们的DPDP。我还是补一张图吧……把我自己绕懵了 看了这张图我相信你就明白了gg数组的含义,接下来考虑如何求解gg数组,初始的话g[i][i]g[i][i]肯定是为0的,所以转移都应该从这个位置开始,即倒序,那么怎么转移呢,接下来就是很恶心的一个地方,计算经过的天数!很多题解里都没写到这个,这里详细计算一下。

    对于gi,jg_{i,j},同样分两种情况讨论,救助或是略过,不管是救助还是略过,都避免不了经过一个区间,就是j+1j+1ii,所以这里可以分而治之,把jjj+1j+1ii这两个分开,gi,jg_{i,j}的转移中应该需要有gi,j+1g_{i,j+1},这里又启示我们进行倒序循环,同样,不管救助jj还是略过,从jj走到j+1j+1的这一天里,区间j+1j+1ii这一段的村庄都会死亡(为村民默哀?)所以答案累加Sumj+1,iSum_{j+1,i}这个可以由前缀和O(1)O(1)求出,到了点j+1j+1后,j+1j+1ii的死亡人数就已经被记在了gi,j+1g_{i,j+1}里,所以可以不用考虑,这是两种情况所共同具有的死亡人数,下面对两种情况分开讨论,如果救治jj的人,那么区间j+1j+1ii的村民就要多死一天,即Sumj+1,iSum_{j+1,i},不救治呢?因为同样的我们跑路的代价都记录在了gi,j+1g_{i,j+1}里边,所以不救治的代价就是在这段时间里jj村死亡的人数,你可能问,别的村难道没有死亡的吗?当然可能会有,但我们已经记录了,所以这里不需要再次加入,首先算一下从jj跑到ii再跑回来所需要的时间,这里举个例子,从4到5要1天,4到6要2天,4到7要3天,所以显然跑路时jj村死亡的人是2(ij)aj2*(i-j)*a_j,2是跑了两遍,iji-j是刚刚推出来的,aja_jjj村日死亡人数,那只有这些吗?当然不是,这只是跑路的代价,根据定义和题意,j+1j+1ii这些村庄均被治愈且均在略过jj后被治愈,所以一个村庄一天,一共就是(i(j+1)+1)aj(i-(j+1)+1)*a_j天,于是我们的gi,jg_{i,j}就有了转移方程 gi,jg_{i,j}=gi,j+1g_{i,j+1}+Sumj+1,iSum_{j+1,i}+minmin(3(ij)aj3*(i-j)*a_jSumj+1,iSum_{j+1,i})

    辅助进行转移的方程有了之后我们就可以进行dpdp的转移了,没错,下边还有很恶心的算时间。

    对于每前ii个村庄,都不可能直接求出他的最小值,所以要枚举中间点jj,即我治愈了前jj个村庄,但是j+1j+1被略过了,所以j+1j+1的治愈是从j+1j+1走到ii再走回来时才被治愈,这一段的代价就是gi,j+1g_{i,j+1},治愈前j个的代价为dpjdp_j,直接累加答案即可,那么最硬核的东西就是i+1i+1nn的这段区间,因为在当前阶段,这段区间内的人是不可能被治愈的,所以一天内的死亡人数是Sumi+1,nSum_{i+1,n},天数呢?根据我之前所推导的,从j+1j+1ii之间反复横跳一来一回一来需要天数3(i(j+1))3*(i-(j+1)),治愈区间j+1j+1ii需要时间(i(j+1)+1)(i-(j+1)+1)

    这里不要忽略了一个地方,就是从jj跑路跑到j+1j+1时还有一天,所以总天数就是3(i(j+1))+i(j+1)+1+13*(i-(j+1))+i-(j+1)+1+1,乘上每天死亡的人数就是最后的代价,累加答案。

    至此,这道省选题就落下了帷幕……什么?你问我最后转移的时候没考虑略过ii就是一路向右的情况?怎么可能,当我枚举到j=i1j=i-1的时候,就相当于转移了这种情况,是吧,所以这个算法是没有问题的,时间复杂度大致为O(n2)O(n^2)可以A掉

    TipsTips::如果你实在看不懂时间怎么算的请拿起笔自己模拟一下吧,很快就能懂,我也尽力了。

    #include<iostream>
    #include<cstring>
    #define ll long long 
    using namespace std;
    const int N=3e3+10;
    ll s[N],g[N][N],dp[N],a[N];
    ll Sum(int l,int r){
        return s[r]-s[l-1];
    }
    int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>s[i];a[i]=s[i];s[i]+=s[i-1];
        }
        for(int i=1;i<=n;i++)
        for(int j=i-1;j;j--)
            g[i][j]=g[i][j+1]+Sum(j+1,i)+min(3LL*(i-j)*a[j],Sum(j+1,i));
        memset(dp,0x3f,sizeof dp);
        dp[0]=0;
        for(int i=1;i<=n;i++)
        for(int j=0;j<i;j++)
            dp[i]=min(dp[i],dp[j]+g[i][j+1]+Sum(i+1,n)*((i-(j+1))*3+i-(j+1)+2));
        cout<<dp[n];
    }
    

    • 1

    信息

    ID
    4785
    时间
    1000ms
    内存
    500MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者