题解[「JOISC 2017 Day 3」长途巴士]

题目

Luogu

Loj

AtCoder

推荐Loj,有翻译。

Sol

一道不那么容易看出来的DP题。难得一见的\(O(n^2)\)式子难推而且要在单调栈上二分斜率

这道题被用来做模拟赛。wo bao li dou bu hui

首先要把\(n^2\)的式子推出来。

排序,\(S_i\)\(S_i\%T\)从大到小排 ,每个乘客按\(D_i\)从大到小排。

\(f_i\)为考虑了前\(i\)个人的最小费用,\(sum_i\)\(1 ~ i\) 乘客 \(C_i\) 的前缀和。

如果我们要把乘客\(i\)送到终点,那么:

\[f_i=f_{i-1}+W(\left\lfloor\dfrac{X-D_i}{T}\right\rfloor+1) \]

意思是后面\(X-D_i​\)的路程都要让他下去接水。

如果我们要一些乘客下车,让\(i\)能刚好留下来

假设让\(j \in (0,i) ,j+1~i\)的所有乘客下车:

\[f_i=min(f_i,f_j+(i-j)*W*mn_i+sum_i-sum_j) \]

\(mn_i\)为到从起点走到特殊的取水点的距离时\(j+1 ~ i\)每个乘客必须要喝水的次数的最小值。

特殊的取水点指的是\(D_i=<S_j\%T<=D_{i+1}\) ,使它能刚好让\(i\)乘客下车。

\[mn_i=min(\left\lfloor\dfrac{S_j}{T}\right\rfloor),D_i=<S_j\%P<=D_{i+1} \]

好了,到这里我们的\(n^2\)就可以啦。

下放代码。

然后我们开始\(O(nlogn)\)的斜率的推导。

拆掉\(min\)

\[f_i=f_j+i*W*mn_i-j*W*mn_i+sum_i-sum_j) \]

只与\(i\)有关的放一边,只与\(j\)有关的放一边:

\[f_j-sum_j=W*j*mn_i+(f_i-i*W*mn_i) \]

括号里的是截距,要让\(f_i\)最小,\(i*W*mn_i\)又是定值,那就是让这个截距最小。

\(x=W*j\)(单调),斜率\(k=mn_i\)

由于\(mn_i\)不单调,所以我们要在单调栈里二分找与\(i\)匹配的斜率。

Y(o)Y!!!

Code

Code1

\(O(n^2)\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=200010;
struct xbk{ll d,c;}a[N];
int n,m;
ll X,w,T,s[N],f[N],mn[N],sum[N],ans=1e18;
inline ll read(){
	ll w=0;
	char ch=getchar();
	while(ch>'9'||ch<'0') ch=getchar();
	while(ch>='0'&&ch<='9'){
		w=(w<<3)+(w<<1)+(ch^48);
		ch=getchar();
	}
	return w;
}
inline bool cmp(xbk a,xbk b){return a.d<b.d;}
int main(){
	X=read(),n=read(),m=read(),w=read(),T=read();
	for(int i=1;i<=n;i++) s[i]=read();
	s[++n]=X;
	for(int i=1;i<=m;i++) a[i].d=read(),a[i].c=read();
	sort(s+1,s+1+n);
	sort(a+1,a+1+m,cmp);
	for(int i=1;i<=m;i++) sum[i]=sum[i-1]+a[i].c;
	a[m+1].d=1e13;
	for(int i=1;i<=m;i++){
		mn[i]=1e13;
		for(int j=1;j<=n;j++)
			if((s[j]%T)>a[i].d&&(s[j]%T)<a[i+1].d){mn[i]=min(mn[i],s[j]/T);}
	} 
	for(int i=1;i<=m;i++){
		f[i]=f[i-1]+w*((X-a[i].d)/T+1);
		if(mn[i]==1e13) continue;
		for(int j=0;j<i;j++)
			f[i]=min(f[i],f[j]+w*(i-j)*mn[i]+sum[i]-sum[j]);
	}
	printf("%lld\n",f[m]+(X/T+1)*w);
	return 0;
}

Code2

\(O(nlogn)\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=200010;
struct xbk{ll d,c;}a[N];
int n,m,top,stk[N];
ll X,w,T,s[N],f[N],sum[N];
inline ll Y(int i){return f[i]-sum[i];}
inline double K(int i,int j){return (Y(i)-Y(j))/(i-j);}
inline ll read(){
	ll w=0;
	char ch=getchar();
	while(ch>'9'||ch<'0') ch=getchar();
	while(ch>='0'&&ch<='9'){
		w=(w<<3)+(w<<1)+(ch^48);
		ch=getchar();
	}
	return w;
}
inline int getpos(ll val){
	int l=1,r=top;
	while(l<r){
		int mid=(l+r)>>1;
		if(K(stk[mid],stk[mid+1])>val) r=mid;
		else l=mid+1;
	}
	return stk[r];
}
inline bool cmp(xbk a,xbk b){return a.d<b.d;}
inline bool cmp1(ll a,ll b){return a%T<b%T;}
int main(){
	X=read(),n=read(),m=read(),w=read(),T=read();
	for(int i=1;i<=n;i++) s[i]=read();
	s[++n]=X;
	for(int i=1;i<=m;i++) a[i].d=read(),a[i].c=read();
	sort(s+1,s+1+n,cmp1);
	sort(a+1,a+1+m,cmp);
	for(int i=1;i<=m;i++) sum[i]=sum[i-1]+a[i].c;
	a[m+1].d=1e13;
	stk[++top]=0;
	for(int i=1,j=0;i<=m;i++){
		f[i]=f[i-1]+((X-a[i].d)/T+1)*w;
		ll mn=1e13;
		while(s[j+1]%T<a[i].d&&j<n) j++;
		while(s[j+1]%T<a[i+1].d&&j<n) mn=min(mn,s[++j]/T);
		if(mn!=1e13){
			int pos=getpos(mn*w);
			f[i]=min(f[i],f[pos]+(i-pos)*w*mn+sum[i]-sum[pos]);
		}
		while(top>1&&K(stk[top],stk[top-1])>K(stk[top],i)) top--;
		stk[++top]=i;
	}
	printf("%lld\n",f[m]+(X/T+1)*w);
	return 0;
}

完结撒花❀

posted @ 2021-03-12 21:23  xxbbkk  阅读(277)  评论(0编辑  收藏  举报