• 【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
    ",dp[p][m]);
        return 0;
    }
    
  • 相关阅读:
    windows笔记进程的句柄
    windows笔记创建线程的函数CreateThread
    c#实现从其他网站抓取imei码信息,手工输入验证码
    Linux下自动修改用户密码的方法(直接通过命令而不是在终端输入密码)
    Redis学习笔记List数据类型
    在Linux(centos)上安装PHP的mongodb扩展
    CI(codeigniter)如何防止sql注入
    MongoDB增加用户认证: 增加用户、删除用户、修改用户密码、读写权限、只读权限
    Sublime Text编辑器如何显示顶部的菜单栏
    C#图片选择器
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7636636.html
Copyright © 2020-2023  润新知