随笔- 22  文章- 0  评论- 10  阅读- 1568 

[CF311B] Cats Transport

思路

我们发现每只小猫有两个对接走时间有影响的参数 DiTi ,而实际上我们只需要知道每只小猫刚好被接走的最小出发时刻是多少。所以a[i] 表示第 i 只小猫刚好被接走的最小出发时刻,显然它等于(该小猫玩耍的时间)-(从开头走到该小猫所在位置所花的时间)。比如这只小猫会玩耍 5 单位时间,从开头走到这里需要 3 单位时间,那么我们在时刻 2 出发就能刚好赶上。

求出 a 数组后,因为小猫本身先后顺序并不影响,所以我们把它升序排个序,那么每次铲屎官都会带走一段连续的小猫。如排序后的 a 数组是 1,2,5,6,8,要带走 8 的那只小猫,就至少要在 8 时刻出发。而在这之前 1256 的小猫都已经玩耍完毕,可以一并带走。

这样,我们就把问题转化成了每个铲屎官要带走哪一段连续区间的小猫的问题,也就是区间划分问题,可以正常地 DP 解决了。

dp[i][j] 表示前 i 个铲屎官接走前 j 只小猫的最少等待时间,考虑它从 dp[i1][k](1kj) 转移过来,也就是说第 j 个铲屎官接走 k~j的小猫。它的出发时间自然等于最后一只小猫 j 所需的时间 a[j](因为要接到区间里的所有猫),则所有猫的等待时间就是 a[j]×(jk)l=k+1ja[l]。由此可得整个的 DP 方程:

dp[i][j]=min{dp[i1][k]+a[j]×(jk)l=k+1ja[l]}

求和部分可以用前缀和解决。然鹅还是要枚举 jk ,不能硬 DP

观察发现该式子有且仅有一个 jk 的乘积项,且 a[i] 具有单调性,满足斜率优化的条件,直接开始斜优。

先把求和化成前缀和相减的形式。 设 sum[i] 表示 a[1]~a[i]的和,即 a 数组的前缀和,可得:

dp[i][j]=min{dp[i1][k]+a[j]×(jk)(sum[j]sum[k])}=min{dp[i1][k]+a[j]×ja[j]×ksum[j]+sum[k]}=min{dp[i1][k]+sum[k]+a[j]×jsum[j]a[j]×k}

考虑在 ij 为定值时候的两个 k1k2k2>k1),要使在 k2 的时候比在 k1的时候更优,必须满足:

dp[i1][k1]+sum[k1]+a[j]×jsum[j]a[j]×k1>dp[i1][k2]+sum[k2]+a[j]×jsum[j]a[j]×k2

消掉等式两边相同的项,化简后可得:

dp[i1][k1]+sum[k1]a[j]×k1>dp[i1][k2]+sum[k2]a[j]×k2

dp[i1][k]+sum[k]f(i,k) 。移项,得:

f(i,k1)f(i,k2)>a[j]×k1a[j]×k2f(i,k1)f(i,k2)>a[j]×(k1k2)f(i,k1)f(i,k2)k1k2<a[j]

这样用一个单调队列维护即可。

代码

#include<bits/stdc++.h>
#define MAXN 100010
#define int long long
#define INF 0x7fffffffffff
using namespace std;
int n,m,q,d[MAXN],a[MAXN],sum[MAXN],x,y,dp[110][MAXN];//前i个铲屎官带走前j只猫
int arr[MAXN],up=0,down=0;
long double f(int x,int i)
{
    return dp[i-1][x]+sum[x]+0.0;
}
long double k(int a,int b,int i)
{
    if(a==b)return -INF;
    return (f(a,i)-f(b,i))/(a-b+0.0);
}
signed main()
{
    scanf("%lld%lld%lld",&n,&m,&q);
    for(int i=2;i<=n;i++)
        scanf("%lld",&d[i]),d[i]+=d[i-1];
    for(int i=1;i<=m;i++)
    {
        scanf("%lld%lld",&x,&y);
        a[i]=y-d[x];
    }
    sort(a+1,a+m+1);
    for(int i=1;i<=m;i++)
        sum[i]=sum[i-1]+a[i];
    for(int i=1;i<=m;i++)
        dp[0][i]=INF;
    for(int i=1;i<=q;i++)
    {
        up=down=0;
        for(int j=0;j<=m;j++)
        {
            while(up<down&&k(arr[up],arr[up+1],i)<=a[j]+0.0)up++;
            int kk=arr[up];
            dp[i][j]=dp[i-1][kk]+a[j]*(j-kk)-sum[j]+sum[kk];
            while(up<down&&k(arr[down-1],arr[down],i)>k(arr[down],j,i))down--;
            arr[++down]=j;
        }
    }
    printf("%lld",dp[q][m]);
    return 0;
}
/*
dp[i][j]=min(dp[i-1][k]+a[j]*(j-k)-(sum[j]-sum[k]))
*/
 posted on   hu_led  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示