CF311B Cats Transport

题意

Zxr960115 is owner of a large farm. He feeds m cute cats and employs p feeders. There's a straight road across the farm and n hills along the road, numbered from 1 to n from left to right. The distance between hill i and (i - 1) is di meters. The feeders live in hill 1.

One day, the cats went out to play. Cat i went on a trip to hill hi, finished its trip at time ti, and then waited at hill hi for a feeder. The feeders must take all the cats. Each feeder goes straightly from hill 1 to n without waiting at a hill and takes all the waiting cats at each hill away. Feeders walk at a speed of 1 meter per unit time and are strong enough to take as many cats as they want.

For example, suppose we have two hills (d2 = 1) and one cat that finished its trip at time 3 at hill 2 (h1 = 2). Then if the feeder leaves hill 1 at time 2 or at time 3, he can take this cat, but if he leaves hill 1 at time 1 he can't take it. If the feeder leaves hill 1 at time 2, the cat waits him for 0 time units, if the feeder leaves hill 1 at time 3, the cat waits him for 1 time units.

Your task is to schedule the time leaving from hill 1 for each feeder so that the sum of the waiting time of all cats is minimized.

\(2 ≤ n ≤ 10^5, 1 ≤ m ≤ 10^5, 1 ≤ p ≤ 100\)

分析

参照ww3113306的博客。

首先我们观察到山与距离其实是没有什么用的,因为对于任意一只猫,我们都可以直接算出如果有一个人要恰好接走它,需要在哪一时刻出发,我们设第i只猫对应的这个时刻为\(t_{i}\).

注意这个\(t_{i}\)是我自己新定义的,跟题目中的没有关系,下面所写的t都是我现在所定义的t,而跟原题面中的t没有任何关系。

然后我们对t数组排个序,于是题意转化为了有m只猫,每只猫有一个权值\(t_{i}\),如果出发时间大于等于\(t_{i}\) ,则可以接到第i只猫。设出发时间为x,则接到第i只猫时,这只猫会等待\(x - t_{i}\)的时间。现在有p个人,要求为每个人指定一个时间使得所有猫的等待时间之和最小。

然后我们继续转化题意。

观察到每个人相当于会选择一只猫i,然后选择在\(t_{i}\)时刻出发,恰好接走这只猫,顺便可以接走其他可以被接走的猫。

为什么是每个人都必须选一只猫呢?

观察到如果一个人出发,没有任何一只猫是恰好被接到的,所有猫都是等了一会再被接走的,那么这个人为什么不早出发一点,恰好接走一些猫呢?这样不仅可以接走和上一种方案相同的猫,还可以减小等待时间。

于是现在题意转化为了有m只猫,每只猫有一个权值\(t_{i}\)。如果第x个人选择了第i只猫,上一个出发的人选了第j只猫,则这个人可以接走[j + 1, i]中的所有猫,并且代价为\(\sum_{k = j + 1}^{i}{t_{i} - t_{k}}\)。现在有p个人,要求为每个人指定一只猫使得所有猫的等待时间之和最小。

先化一下式子:(其中s[i]表示\(\sum_{k = 1}^{i}{t[k]}\)
\[ \sum_{k = j + 1}^{i}{t_{i} - t_{k}} \\ = \sum_{k = j + 1}^{i}{t_{i}} - \sum_{k = j + 1}^{i}{t_{k}} \\ = (i - j) t_{i} - (s[i] - s[j])=(i−j)t \]
于是我们设f[i][j]表示DP到第i个人,这个人选择了第j只猫的最小代价。

然后暴力枚举i,j,转移的时候再暴力枚举前一个人选了哪只猫即可。

但是这样的复杂度是\(pm^2\)的,观察到我们优化到\(pm\)就可以过了,又注意到式子中有一个跟i相关的量和一个跟j相关的量的乘积,我们考虑一下斜率优化。

我们先化一波式子。
\[ f[i][j] = f[i - 1][k] + (j - k) t_{j} - (s[j] - s[k]) \]
然后设\(x>y\),且\(x\)\(y\)优,则
\[ \frac{f[i-1][x]+s[x]-f[i-1][y]-s[y]}{x-y}<t[i] \]
小于单调增,下凸包+单调队列。

时间复杂度\(O(pm)\)

代码

我还是第一次做到多次斜率优化,并且弹出的和入队的比较内容不一样,感觉理解又深了一层。CF上面的题的质量很高,具有创新性,可以尝试。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<ctime>
#include<cstring>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
    rg T data=0;
    rg int w=1;
    rg char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        data=data*10+ch-'0';
        ch=getchar();
    }
    return data*w;
}
template<class T>T read(T&x)
{
    return x=read<T>();
}
using namespace std;
typedef long long ll;

co int M=1e5+1,P=101;
ll d[M],t[M],s[M];
ll f[P][M];

ll Up(int i,int x,int y) // edit 1: use ancient information,push anciently
{
    return f[i-1][x]+s[x]-f[i-1][y]-s[y];
}

ll Down(int x,int y)
{
    return x-y;
}

ll Cal(int i,int j,int k)
{
    return f[i-1][k]+(j-k)*t[j]-s[j]+s[k];
}

int q[M];

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    int n=read<int>(),m=read<int>(),p=read<int>();
    for(int i=2;i<=n;++i)
        d[i]=d[i-1]+read<int>();
    for(int i=1;i<=m;++i)
    {
        int h=read<int>();
        t[i]=read<int>()-d[h];
    }
    sort(t+1,t+m+1);
    for(int i=1;i<=m;++i)
        s[i]=s[i-1]+t[i];
        
    for(int j=1;j<=m;++j)
        f[1][j]=j*t[j]-s[j];
    for(int i=2;i<=p;++i)
    {
        int head=0,tail=0;
        q[tail++]=0;
        for(int j=1;j<=m;++j)
        {
            while(head+1<tail&&Up(i,q[head+1],q[head])<=t[j]*Down(q[head+1],q[head]))
                ++head;
            f[i][j]=Cal(i,j,q[head]);
            while(head+1<tail&&Up(i,j,q[tail-1])*Down(q[tail-1],q[tail-2])<=Up(i,q[tail-1],q[tail-2])*Down(j,q[tail-1]))
                --tail;
            q[tail++]=j; // edit 2:push j
        }
    }
    printf("%I64d\n",f[p][m]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/10199185.html