• CodeForces 311B


    洛谷又关发题解入口了((

    洛谷题目页面传送门 & CodeForces题目页面传送门

    一条直线上有(n)座山,从左往右编号依次为(1sim n),第(i-1,i)座山的距离为(d_i)个单位时间走的路程。有(m)只猫,第(i)只去第(h_i)座山玩到时刻(t_i)。有(s)个铲屎官,每个铲屎官可以从任意时刻出发,从第(1)座山往第(n)座山走一趟,中间不停,抱走所有的未被抱走的且玩完的猫。第(i)只猫的等待时间为它被抱走的时刻减去(t_i)。求最小的所有猫的总等待时间。

    (ninleft[2,10^5 ight],minleft[1,10^5 ight],sin[1,100])

    考虑某铲屎官从时刻(x)出发,那么显然第(i)只猫可能被他抱走当且仅当(x+sumlimits_{j=2}^{h_i}d_jgeq t_i)。关于(i)的放一边,得(t_i-sumlimits_{j=2}^{h_i}d_jleq x)。令(a_i=t_i-sumlimits_{j=2}^{h_i}d_j)。若真的被此铲屎官抱走了,那么等待时间为(x-a_i)

    于是问题转化为了:有(m)个数,第(i)个数为(a_i)。要求给出一个数集(S(|S|=s))(forall iin[1,m]),计算(S)中最小的(geq a_i)的数减去(a_i),答案为这些差的和。求最小的和。

    考虑将(a)排序。那么很容易得出结论:“(S)中最小的(geq a_i)的数”是非严格单调递增的。于是减去的数们是由不超过(s)个严格单调递增的相等段组成的。

    考虑DP。设(dp_{i,j})表示前(j)个数,用了(i)个相等段(可以为空)时的最小的差的和。边界:(dp_{0,j}=egin{cases}0&j=0\+infty&j>0end{cases}),目标:(dp_{s,m})。转移的话,考虑枚举倒数第二个相等段的右端点(倒数第一个相等段的右端点为(j)),由于(a)的单调性,最后一个相等段的值显然为(a_j)最优。考虑预处理出(a)的前缀和数组(Sum),那么状态转移方程很容易列出:(dp_{i,j}=minlimits_{k=0}^j{dp_{i-1,k}+a_j(j-k)-(Sum_j-Sum_k)})

    直接转移(mathrm O!left(m^2s ight))。这一看就是个斜率优化的模型,考虑对每个(i)做一次斜率优化:对于(2)个决策(k<o)(k)(o)优当且仅当

    [dp_{i-1,k}+a_j(j-k)-(Sum_j-Sum_k)<dp_{i-1,o}+a_j(j-o)-(Sum_j-Sum_o) ]

    变形后,由(k<o)(k-o<0),除过去不等号变方向,得

    [f(k,o)=frac{(dp_{i-1,k}+Sum_k)-(dp_{i-1,o}+Sum_o)}{k-o}>a_j ]

    再基础不过了根据(a)的单调性,单调队列维护下凸壳即可。

    时间复杂度(mathrm O(n+mlog m+ms))。空间可以滚动数组优化到(mathrm O(n+m))

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    const int inf=0x3f3f3f3f3f3f3f3f;
    const int N=100000,M=100000;
    int n,m,s;
    int d[N+1];
    int a[M+1],Sum[M+1];
    int dp[2][M+1];//滚动数组优化 
    int q[M],head,tail;//单调队列 
    double f(int i,int k,int o){//斜率 
    	return 1.*((dp[i-1&1][k]+Sum[k])-(dp[i-1&1][o]+Sum[o]))/(k-o);
    }
    signed main(){
    	cin>>n>>m>>s;
    	for(int i=2;i<=n;i++)cin>>d[i],d[i]+=d[i-1];
    	for(int i=1;i<=m;i++){
    		int h,t;
    		cin>>h>>t;
    		a[i]=t-d[h];
    	}
    	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;//DP边界 
    	for(int i=1;i<=s;i++){//对每个i
    		head=tail=0;//清队列 
    		q[tail++]=0;//加入决策0 
    		for(int j=1;j<=m;j++){
    			while(head+1<tail&&f(i,q[tail-2],q[tail-1])>=f(i,q[tail-1],j))tail--;//维护队尾单调性 
    			q[tail++]=j;//加入决策j 
    			while(head+1<tail&&f(i,q[head],q[head+1])<=a[j])head++;//弹出过时决策 
    			dp[i&1][j]=dp[i-1&1][q[head]]+a[j]*(j-q[head])-(Sum[j]-Sum[q[head]]);//计算DP值 
    		}
    	}
    	cout<<dp[s&1][m];//目标 
    	return 0;
    }
    
  • 相关阅读:
    Python正则表达式指南
    Python中的HTMLParser、cookielib抓取和解析网页、从HTML文档中提取链接、图像、文本、Cookies(二)
    Python中的urlparse、urllib抓取和解析网页(一)
    __name__ = '__main__'
    odoo context
    Python xlwt模块
    python中使用xlrd、xlwt操作excel
    odoo 下 get_object_reference 函数
    Python运算符
    jQuery实现contains方法不区分大小写的方法教程
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/CodeForces-311B.html
Copyright © 2020-2023  润新知