【Codeforces Round #185 (Div. 2) D】Cats Transport

【链接】 链接
【题意】


有n座山,m只猫。
每只猫都在其中的一些山上玩。
第i只猫在h[i]山上玩,且会在t[i]时刻出现在山脚下(然后就一直在那里等)
然后有p个人。
它们听从你的安排。
在某个时刻从1号山出发,依次经过每座山,如果有猫在山脚。那么它会顺便把它们带走。
(山与山之间有距离,然后人移动的速度是1单位每秒)
每个人可以无限数量地拿猫。
问你所有猫的总等待时间的最小值是多少。
(人可以在负数的时间出发)

【题解】


把山与山之间的距离求成前缀和。
即d[i]表示1..i之间的距离
然后对于第i只猫,设a[i] = t[i] - d[h[i]];
则a[i]就表示恰好走到h[i]的时候,猫恰好下来,应该从何时从1出发。
按照a[i]升序排一下。
那么
现在相当于,让你在这m只猫里,选连续的p个段。分完所有的m只猫。
且所有猫的等待时间总和最短。
这个可以用区间DP来写。
每个区间里的猫都在区间的右端点的猫的时间出发去取。然后算一下代价就好。
设s[i] = a[1] + a[2] +...+a[i],dp[i][j]表示i个人管了前j只猫的最小值
则dp[i][j] = min{dp[i-1][x] + a[j](j-x)-(s[j]-s[x])};
这样的复杂度是
\(O(p*m^2)\)
考虑斜率优化。
假设x<y<j
且y优于x

dp[i-1][y] + a[j]
j - a[j]y-s[j]+s[y] < dp[i-1][x] + a[j]j - a[j]x-s[j]+s[x]

dp[i-1][y]+s[y]-a[j]
y<dp[i-1][x]+s[x]-a[j]*x
也即
\(\frac{dp[i-1][y]+s[y]-(dp[i-1][x]+s[x])}{y-x} < a[j]\)
而a[j]我们已经升序排了,是单调递增的。
那么就是一个经典的斜率优化了。
优化过后。
复杂度能降为\(O(p*m)\)级别。

【错的次数】


在这里输入错的次数

【反思】


在这里输入反思

【代码】

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 1e5;
const int P = 1e2;

int n,m,p,d[N+10],a[N+10];
ll s[N+10];
ll dp[P+10][N+10];
int dl[N+10],head,tail;

double ju(int i,int x,int y)
{
    return (1.0)*(dp[i-1][y] + s[y] - (dp[i-1][x]+s[x]))/(1.0*(y-x));
}

int main()
{
    //freopen("F:\\rush.txt","r",stdin);
    scanf("%d%d%d",&n,&m,&p);
    for (int i = 2;i <= n;i++)
    {
        scanf("%d",&d[i]);
        d[i]+=d[i-1];
    }

    for (int i = 1;i <= m;i++)
    {
        int h,t;
        scanf("%d%d",&h,&t);
        a[i] = t - d[h];
    }

    sort(a+1,a+1+m);

    for (int i = 1;i <= m;i++)
        s[i] = s[i-1] + a[i];

    for (int i = 0;i <= p;i++)
        for (int j = 1;j <= m;j++)
            dp[i][j] = 1e17;
    dp[0][0] = 0;

    for (int i = 1;i <= p;i++)
    {
        head = 1,tail = 1;
        dl[1] = 0;
        for (int j = 1;j <= m;j++)
        {
            while (head < tail && ju(i,dl[head],dl[head+1])<a[j]) head++;
            dp[i][j] = min(dp[i][j],dp[i-1][dl[head]] + 1LL*a[j]*(j-dl[head])-(s[j]-s[dl[head]]));
            while (head < tail && ju(i,dl[tail-1],dl[tail])>ju(i,dl[tail],j)) tail--;
            dl[++tail] = j;
        }
    }
    printf("%lld\n",dp[p][m]);
    return 0;
}

转载于:https://www.cnblogs.com/AWCXV/p/7636636.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值