CSU1963: Feed the rabbit

CSU1963: Feed the rabbit

Description

There are n holes in a line,the distance between the hole labeled i and i+1 is di。There are also m rabbits, the rabbit labeled i comes to hole ki in time ti. Now you have p people,you can arrange them start at any time(even before time point 0) from the first hole, their speed is 1. They head to hole n without stopping and feed the rabbit on their way without any cost of time. Now you must make every rabbit be fed just one time. You need to minimize the sum of time between arriving and fed for all rabbits.

Input

The first line includes 3 int numbers n,m,p。 The next line includes n-1 numbers,d1,d2,…dn-1。 Next m lines, every line includes 2 numbers, hi and ti, the i th line means that the i th rabbit will arriving at hole hi in time ti.

Output

One number, the minimized sum of waiting time of all rabbits.

Sample Input

4 6 2
1 3 5
1 0
2 1
4 9
1 10
2 10
3 12

Sample Output

3

Hint

30% n,m<=300 100% n,m<=10^5,1<=p<=100,1<=di<=10000,1<=hi<=n,0<=ti<=1090<=ti<=109

Source

2017湖南多校第十三场

Author

alpc

  题目大意就是说有n个坑m只兔子,每只兔子会在ti时间后的hi坑冒头(处于等待状态),有p个人,每个人都可以在任意时间内以1的速度走向n洞并喂食沿途的兔子,求兔子最少的总等待时间

  思路是这样想的:对应每只兔子,都有一个刚好喂到它的人的出发时间(最早),同时实际喂食它的人的出发时间减去这个时间也就是这只兔子等待的时间,同时每个人都一定从1走到n,所以我们就预处理出这个时间。

  以样例为例,处理并排序后的时间为0,0,0,8,9,10,这时问题可以抽象成这样:如何将这样的数组分成至多p段,设每段记为

a0,a1,a2,...,an
,使得各段对应
anna1a2...an
之和最小(
anna1a2...an
正好就代表了若一个人喂到了这些兔子,那么这些兔子所需要等待的时间总长,可以想想这是为什么)。

  如0,0,0,8,9,10,将其分为0,0,0和8,9,10两段,前一段的

anna1a2...an
为0,后一段为3,这就是最小值了。

  为了方便快速求出分段为(i,j)所对应的值,预处理求数组的前缀和,令

sum[n]=i=1na[i]

  于是这一段所对应的代价就是

a[i](ij)(sum[i]sum[j])

  比较容易想到这个题要用动态规划,状态转移方程如下:

  

f[p][i]=min1ji{f[p1][j]+a[i](ij)(sum[i]sum[j])}

  其中p为人数,i为喂食到的兔子的序号(按对应出发时间排)

  花括号内的式子展开为

f[p1][j]+a[i]ia[i]jsum[i]+sum[j]
,观察发现中间两项仅和i有关,于是变为

  

f[p][i]=a[i]isum[i]+min1ji{f[p1][j]a[i]j+sum[j])}

  若已求得

j
为最优解,那么对于任意的
1ki
kj
,都有

  

f[p1][j]+sum[j]a[i]j<f[p1][k]+sum[k]a[i]k

  令

yj=f[p1][j]+sum[j],xj=j,yk=f[p1][k]+sum[k],xk=k
,则有
yjykxjxk<a[i]

  首先将会形成上凸的点删去,因为这些点必然不会是最优解的位置,思路与《浅谈数形结合思想在信息学竞赛中的应用》中所说到的是差不多的。

  在之后剩余的点中,我们就需要找到每个

i
对应的
a[i]
所对应的最优解,又由上面的式子我们知道对应的最优解就是使得斜率
kj,j1
最大但是又不超过
a[i]
,由于这一题我们的
a[i]
已经排序过了,而留下的点又形成一个下凸的折线,故在计算的时候可以利用单调队列对求解过程进行优化,使得最后的时间复杂度为O(n)。

AC代码

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
#include <string>
#define LL long long
#define ULL unsigned long long
#define mem(a,n) memset(a,n,sizeof(a))
#define fread freopen("in.txt","r",stdin)
#define fwrite freopen("out.txt","w",stdout)
#define N 100100
#define INF 0x3f3f3f3f
#define eps 1e-9
using namespace std;
LL dis[N],sum[N],dp[110][N],ti[N],que[N];
inline LL dy(int id,int j,int k){
    return dp[id-1][j]+sum[j]-dp[id-1][k]-sum[k];
}
inline LL dx(LL x,LL y){
    return x-y;
}
int main()
{
    ios::sync_with_stdio(false);
    int n,m,p,hole,tim;
    while(cin>>n>>m>>p){
        dis[1]=0;
        for(int i=2;i<=n;++i){
            cin>>dis[i];
            dis[i]+=dis[i-1];
        }
        for(int i=1;i<=m;++i){
            cin>>hole>>tim;
            ti[i]=tim-dis[hole];
        }
        sort(ti+1,ti+m+1);
        for(int i=1;i<=m;++i){
            sum[i]=sum[i-1]+ti[i];
        }
        for(int i=1;i<=m;++i){
            dp[1][i]=ti[i]*i-sum[i];
        }
        for(int i=2;i<=p;++i){
            int frt=0,tail=-1;
            for(int j=1;j<=m;++j){
                while(frt<tail&&dy(i,que[frt+1],que[frt])<
                      ti[j]*dx(que[frt+1],que[frt])){
                    ++frt;
                }
                while(frt<tail&&dy(i,que[tail],que[tail-1])*dx(j,que[tail])>=
                      dy(i,j,que[tail])*dx(que[tail],que[tail-1])){
                        --tail;
                }
                que[++tail]=j;
                int v=que[frt];
                //cout<<dp[i-1][v]<<' '<<sum[v]<<' '<<ti[j]<<' '<<v<<endl;
                dp[i][j]=dp[i-1][v]+sum[v]-ti[j]*v+ti[j]*j-sum[j];
                //printf("i_%d j_%d %lld\n",i,j,dp[i][j]);
            }
        }
        //for(int i=1;i<=p;i++)
        //for(int j=0;j<=m;j++) printf("i_%d j_%d %lld\n",i,j,dp[i][j]);
        cout<<dp[p][m]<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值