zoukankan      html  css  js  c++  java
  • noi.ac #534 猫

    题目链接:戳我

    【问题描述】

    有n座山,m只猫和p个工作人员。山从左往右编号为1∼n,山i和i−1之间的距离是di米。

    有一天,猫都到山上去玩了:第i只猫会到山hi去,并一直玩到时间ti,之后就在那座山等待工作人员来接它。

    每个工作人员的线路都是从1走到n,并带走沿途任意只在等待的猫。工作人员速度为每单位时间1米,不能在山上停留。

    例如,假设有两个山丘,d2=1,有一只猫要到山2去,在t=3结束它的玩耍。如果工作人员在时间2或时间3离开山1,则他可以带走这只猫,但如果在时间1离开山1,他就不能带走它。如果工作人员在时间2离开山1,则猫等待他0个时间单位,如果工作人员在时间3离开山1,则猫等待他1个时间单位。

    你的任务是安排每个工作人员从1出发的时间(整数,可以是负数),使所有猫的等待时间总和最小。

    【输入格式】

    第一行三个整数n,m,p,表示山、猫、工作人员的数目。

    第二行n−1个整数表示d2∼dn。

    后面m行,每行两个数hi,ti。

    【输出格式】

    一个整数,表示所有猫的等待时间总和的最小值。

    【数据规模】

    40%的数据,m≤1000,p≤100。

    80%的数据,m≤5000,p≤1000。

    100%的数据,1≤m≤50000,1≤p≤1000。

    对于所有数据,2≤n≤1e5,1≤di≤100,1≤hi≤n,0≤ti≤10e5。


    斜率优化。
    我们考虑每个猫的结束时间减去它的坐标,就相当于所有猫都在节点1,只是结束的时间不同了。
    我们再把这个结束的时间排序一下,就可以设(dp[i][j])表示前i只猫,被j个饲养员带走的最小代价了。
    转移方程为:(dp[i][j]=min{dp[i][j],dp[p][j-1]+node[i].num*(i-p)-(sum[i]-sum[p])})
    其中(sum[i])表示前i只猫的等待时间前缀和,(node[i].num)表示该猫等效于在1节点的开始等待时刻。

    然后这个朴素DP是40分的。

    现在考虑斜率优化:
    把式子移项一下:
    (dp[i][j]+node[i].num*p=dp[p][j-1]+node[i].num*i-sum[i]+sum[p])
    这就有了(b+kx=y)的形式
    维护下凸壳即可。

    下面这份代码被卡常了??只有90分嘤嘤嘤

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define MAXN 100010
    #define ll long long
    using namespace std;
    inline ll read()
    {
        ll x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
        return x*f;
    }
    ll n,m,p,tail,head;
    ll d[MAXN],dis[MAXN],sum[MAXN],dp[50010][1010],q[MAXN];
    struct Node{ll p,num;}node[MAXN];
    inline double y(int i,int p){return dp[i][p]+sum[i];}
    inline double k(int i,int j,int p)
    {
    	if(i==j) return 1e9;
    	return (y(i,p-1)-y(j,p-1))/(i-j);
    }
    inline bool cmp(struct Node x,struct Node y){return x.num<y.num;}
    int main()
    {
        n=read(),m=read(),p=read();
    	for(int i=2;i<=n;i++) 
            d[i]=read(),dis[i]=dis[i-1]+d[i];
    	for(int i=1;i<=m;i++)
        {
            int x;
    		node[i].p=read(),x=read();
            node[i].num=x-dis[node[i].p];
    	}
    	sort(node+1,node+1+m,cmp);
    	for(int i=1;i<=m;i++) sum[i]=sum[i-1]+node[i].num;
    	memset(dp,0x3f,sizeof(dp));
        dp[0][0]=0;
    	for(int j=1;j<=p;j++)
        {
    		tail=head=0;
    		for(int i=1;i<=m;i++)
            { 
    			while(head<tail&&node[i].num>k(q[head],q[head+1],j)) head++;
    			dp[i][j]=dp[q[head]][j-1]+node[i].num*i-node[i].num*q[head]-sum[i]+sum[q[head]];
    			while(head<tail&&k(q[tail-1],q[tail],j)>k(q[tail],i,j)) tail--;
    			q[++tail]=i;
    		}
    	}
    	printf("%lld
    ",dp[m][p]);
    	return 0;
    }
    
  • 相关阅读:
    stdin stdout stderr
    stat
    ssh-keyscan
    START TRANSACTION
    ssh-keygen
    ssh-agent
    ssh-add
    虚拟化之lxc
    Web 在线文件管理器学习笔记与总结(1)初始文件以及获取首层目录信息
    Java实现 LeetCode 97 交错字符串
  • 原文地址:https://www.cnblogs.com/fengxunling/p/11143111.html
Copyright © 2011-2022 走看看