首页 > 其他分享 >joisc2017 D3T1 长途巴士 题解

joisc2017 D3T1 长途巴士 题解

时间:2024-08-03 11:31:29浏览次数:12  
标签:joisc2017 题解 踢掉 D3T1 llt tail pl llb dp

joisc2017 D3T1 长途巴士 题解

这是学校 ACM 欢乐赛的题,赛时想到做法了,但是因为我各种唐诗没写出来

翻了

转化题面

我们发现,只可以踢掉检查站前面一个连续段的接水人,并且不能踢掉司机,考虑画出对整个路程表示的线段

上图几个小点是检查点,考虑在每个检查点之前踢掉一段的人,容易发现不能踢掉司机,后踢掉一个人一定不如在前面踢掉他优,所以区间没有交

考虑预处理出每个检查点踢掉每个人能节省的水费,直接 dp 即可,然而问题在于本题 \(10^{12}\) 的决策数

我们发现其实我们只关心踢掉哪些人和省了多少水,第二个已经预处理出来了,所以我们将人按照 \(D_i\) 排序,同样每次决策只会选择后面的一段,把 \(S_i \mod t\) 同时排序,发现其实贡献可以拆成两个部分,分别是

  1. 右端点(即检查点)的权值,也就是踢掉每个人的水费减少量
  2. 其他点的权值,也就是踢掉一个人要退的钱

暴力

我们容易写出转移方程

\[dp_i=\min_{1 \le k \le i}((k-pl_i-1)p_i+dp_{f(k)}+g(k,pl_i),dp_{i-1}) \]

稍微解释一下,\(f(x)\) 表示在 \(x\) 前面的第一个决策点,直接预处理。\(g(x,y)\) 表示将所有 $ x \le D \le y$ 的全部人赶下车要得到的钱,\(pl_i\) 是第 \(i\) 个决策点的位置,\(p_i\) 是能省的钱

发现可以预处理后缀和算 \(g(x,y)\),同时我们为了避免之后 dp 过程中用 $x \mod t $ 与 \(k\) 分讨,也把这个扔进去。

把一个人都不踢的钱和最后的 dp 值相加即为答案

复杂度 \(O(n^2)\)

优化

斜率优化板子

当做斜率优化笔记写吧

把式子展开,发现长这个样子(\(c\) 是后缀和)

\[dp_i=kp_i-pl_ip_i-p_i+dp_{f(k)}+c_k-c_{pl_i+1} \]

只有右边最前面那个是关于 \(i\),\(k\) 一次函数的乘积项,显然可以斜率优化

按照那个式子移个项

\[c_{pl_i+1}+pl_ip_i+p_i+dp_i-kp_i=dp_{f(k)}+c_k \]

固定 \(i\),左边那一坨都是定值,为了最小化 \(dp_i\),最小化那一整坨就完了,所以我们维护一个斜率单调递增的下凸壳,注意斜率是 \(p_i\),我们之前为了让 \(pl_i\) 有序,从而确定 dp 顺序,所以将决策排序了,\(p_i\) 被打乱了,并不单调,要在单调栈里面二分

代码

点击查看代码
#include<bits/stdc++.h>
#define llt long long
#define llb long double
using namespace std;
const llt N=200010; 
llt x,n,m,w,t,d[N],c[N],s[N],pl[N],p[N],f[N],dp[N],us,siz,sum,ans,tot,X[N],y[N],P,xx,yy,head=1,tail,last;
pair<llt,llt> pr[N],pi[N];
llb calc(llb xa,llb ya,llb xb,llb yb){return (yb-ya)/(xb-xa);}
llt find(llb K)
{
    llt l=head,r=tail,mid;
    while(l<r)
    {
        mid=l+r>>1;
        if(calc(X[mid],y[mid],X[mid+1],y[mid+1])<K)
            l=mid+1;
        else r=mid;
    }
    return l;
}
int main()
{
    #ifdef LOCAL 
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    scanf("%lld%lld%lld%lld%lld",&x,&n,&m,&w,&t); 
    for(int i=1;i<=n;i++)   scanf("%lld",&s[i]);
    for(int i=1;i<=m;i++)   
    {
        scanf("%lld%lld",&pr[i].first,&pr[i].second);
        if(x%t>=pr[i].first)    sum+=1+x/t;
        else sum+=x/t;
    }
    sum+=x/t+1;
    ans=sum*w;
    for(int i=1;i<=n;i++){pl[i]=s[i]%t,p[i]=x/t*w-(s[i]/t)*w;}
    pl[++n]=x%t;p[n]=0;sort(pr+1,pr+1+m);
    for(int i=1;i<=n;i++)   pl[i]=upper_bound(pr+1,pr+1+m,make_pair(pl[i],0ll))-pr-1;
    for(int i=1;i<=n;i++)   pi[i]=make_pair(pl[i],p[i]);
    sort(pi+1,pi+1+n);
    for(int i=1;i<=m;i++)   d[i]=pr[i].first,c[i]=pr[i].second;
    for(int i=1;i<=m;i++)   if(d[i]<=x%t)  c[i]-=w;
    for(int i=m;i>=1;i--)   c[i]=c[i+1]+c[i];
    for(int i=1;i<n;i++)
    {
        if(pi[i].first==pi[i+1].first)  continue;
        pl[++siz]=pi[i].first;
        p[siz]=pi[i].second;
    }
    pl[++siz]=pi[n].first;p[siz]=pi[n].second;
    for(int i=0;i<=siz;i++)
        for(int j=pl[i]+1;j<=pl[i+1];j++)
           f[j]=i;
    P=1;while(pl[P]==0)    P++;
    for(int i=1;i<=m;i++)
    {
        xx=i,yy=dp[f[i]]+c[i];
        while(head<tail&&calc(X[tail-1],y[tail-1],X[tail],y[tail])>=calc(X[tail],y[tail],xx,yy))    tail--;
        X[++tail]=xx,y[tail]=yy;
        if(i==pl[P]) {us=find(-p[P]);dp[P]=y[us]-c[pl[P]+1]-pl[P]*p[P]-p[P]+p[P]*X[us];dp[P]=min(dp[P],dp[P-1]);P++;}
    }
    cout<<dp[siz]+ans<<endl;
    return 0;
}

标签:joisc2017,题解,踢掉,D3T1,llt,tail,pl,llb,dp
From: https://www.cnblogs.com/hzoi-wang54321/p/18340234

相关文章

  • ABC269F 题解
    注意到第\(i\)行和第\(i+2\)行被删除的格子的排列顺序相同,格子上的数差了\(2m\)。于是处理出第\(i,i+1\)行的答案\(a_i,a_{i+1}\),有值的格子的个数\(c_i,c_{i+1}\)。令\(s(i)=\dfrac{(i-1)i}2\),也就是\(\sum_{j=1}^{i-1}j\)。第\(i,i+2,i+4\cdots\)行的和:\(a_i\t......
  • ABC268F 题解
    考虑贪心。设字符串\(S\)里数字之和为\(S_d\),X的个数为\(S_c\)。考虑相邻的两个字符串\(A,B\)的贡献:考虑临项交换,这只影响到相邻两个串的相互贡献。注意到交换\(A,B\)只会影响到\(B_dA_c,A_dB_c\),那么产生的贡献\(\Delta=B_dA_c-A_dB_c\)。因为对于最优解,\(\Delta......
  • ACM第三次测试部分题解(B,C,D,E,J)
    (B)N^N(简单快速幂模板,直接套用就行)点击查看代码#include<iostream>usingnamespacestd;longlonga,b,n;intmain(){cin>>n;while(n--){scanf("%lld",&a);signedlonglongA=a%10,sum=1;while(a)......
  • AGC039B 题解
    因为一条边只能在\(V_i,V_i+1\)之间,如果把\(V_1,V_3,\cdots\)看作一部分,\(V_2,V_4,\cdots\)看作一部分,这就是个二分图。考虑一个二分图怎么“展开”成最多的集合。考虑答案上界。上界是点对最短路的最大值加一。如果集合个数超过上界,那么一定存在一条边跨越多个集合。猜......
  • AGC033C 题解
    这里树的直径\(l\)定义为两个有硬币的点的最长距离。做一次操作后,树的直径一定会变短。我们发现,如果在直径端点做操作,直径减一。在非直径端点操作,直径减二。于是变成了一个威佐夫博弈,但是要注意的是,在直径长度为0,1,2的时候,每次都只能让直径减一。因为直径长度为1,先手必......
  • AGC059B 题解
    对于一种构造,考虑怎么表示。可以把相邻不同颜色建图连边。注意到答案不可能小于\(n-1\),否则图不联通,显然不可能。考虑什么情况下是\(n-1\)。图是一棵树。考虑怎么构造出一棵树。因为一种颜色出现次数大于等于这个点的度数,可以考虑可以确定叶子。把剩余度数最小的往最大的......
  • AGC056A 题解
    首先考虑\(n\equiv0\pmod3\)的情况。非常简单,然后考虑\(n\equiv1\pmod3\)的情况。只要把多出来的第一列第一行填满就行了。还要比原来情况多一个连通块。然后考虑\(n\equiv2\pmod3\)的情况。手玩一下,再往左上角添一点东西就行了。#include<bits/stdc++.h>us......
  • AGC044B 题解
    考虑每次更新就跑一遍bfs。\(O(n^4)\),复杂度不对?但是注意到\(dis\)的最大值也就是\(n\),每次更新\(dis\)至少减一。所以最大值最多被更新\(n\)次,一共更新\(O(n^3)\)次,复杂度是对的。直接暴力。#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;......
  • ABC267F 题解
    注意到,对于一棵树\(T\)的任一直径\(a-b\),对于任意一点\(u\),离\(u\)最远的点一定是\(a\)或\(b\)。考虑反证:如图,如果存在点\(c\)使得\(dis(u,c)>\max(dis(u,a),dis(u,b))\)。如图,\(a-b\)为直径,\(d2>d1\)。因为有\(d4>d3+d2\),所以有\(d2+d3+d4>2d2+2d3>d1+d2\),所以......
  • ABC266F 题解
    输入的图是一颗基环树。对于\(x,y\),如果把环上的边去掉,得到的森林里\(x,y\)仍然在同一颗树内,那么显然只有一条路。否则一定要经过环,有两条路。于是dfs或着拓扑排序找环即可。#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constintN=2e5+......