【题解】Cats Transport (斜率优化+单调队列)
# | When | Who | Problem | Lang | Verdict | Time | Memory |
---|---|---|---|---|---|---|---|
55331572 | Jun/09/2019 19:18UTC+8 | Winlere | D - Cats Transport | GNU C++11 | Accepted | 405 ms | 84200 KB |
思考的过程很艰难,想清楚之后就不难做了。记录一下思路过程。
时间 | 事件 |
---|---|
14:00 | 开始审题 |
14:15 | 手玩样例 |
14:30 | Observe ( imes 1):一个喂养员喂连续的一片 |
14:50 | Observe( imes2):一个喂养员喂的连续的一片一定是按照(t(i)-dis(1,h_i))的升序最优 |
15:00 | Brute Force( imes 1): (O(n^2))DP,(dp(i,j))考虑了(i)个喂养员,带回前(j)只猫(按照(t(i)-dis(1,h_i))排序) |
15:30 | Debug ( imes 1): (t(i)-dis(1,h_i))不需要对(0)取(max) |
16:30 | Solution( imes 1) 单调队列 |
16:50 | Solution( imes2) 斜率优化+单调队列 |
吃饭 | 差点被阿 |
19:18 | Accepted |
想的时间占了很多,幸运的是一开始的(O(n^2))DP的方向是对的,一个小时写出暴力还是很赚的。就是后面想什么单调队列,应该可以直接想到斜率优化的,(n^2)转(n log n)太常见了。
有两个Observe比较显然就不证明了。(t_i-dis(1,i))可以代表的含义是喂养员最早出来的时刻。
(O(n^2))的转移:
[dp(i,j)=min{dp(i-1,j),dp(i-1,k)+(j-k) imes t_j-(sum(j)-sum(k))}
\
sum(i)=Sigma_{j=1}^it_j-dis(1,j)
]
拆开(j,k)直接变成一个斜率优化的套路式。
(x_k=k,y_k=dp(i-1,k)+sum(k))
原式变为:
[y_k=t_jx_k+(dp(j)-j imes dis(1,j)+sum(j))
]
查询一个截距最小值。好像要单调队列维护。查到哪个(k)转移套到原式就好了。
不过这里复杂度貌似(O(n)(kle 100))
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std; typedef long long ll;
inline ll qr(){
register ll ret=0,f=0;
register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=1e5+5;
int n,m,p;
int dis[maxn];
ll sumd[maxn];
ll sumdata[maxn];
ll x[maxn];
ll y[maxn];
ll dp[101][maxn];
struct NODE{
int pos,time;
ll limit;
NODE(){limit=pos=time=0;}
inline bool operator <(const NODE&a)const{return limit<a.limit;}
inline void scan(){
pos=qr();time=qr();
limit=time-sumd[pos];
}
}data[maxn];
typedef deque<int>::iterator it;
deque < int > q;
int main(){
n=qr();m=qr();p=qr();
for(register int t=2;t<=n;++t)
sumd[t]=(dis[t]=qr())+sumd[t-1];
for(register int t=1;t<=m;++t)
data[t].scan();
sort(data+1,data+m+1);
for(register int t=1;t<=m;++t) sumdata[t]=data[t].limit+sumdata[t-1];
memset(dp,5,sizeof dp);
dp[0][0]=0;
it ita;
for(register int i=0;i<=m;++i)
x[i]=i;
for(register int t=1;t<=p;++t){
for(register int i=0;i<=m;++i){
dp[t][i]=dp[t-1][i];
y[i]=dp[t-1][i]+sumdata[i];
}
q.clear();
for(register int i=1;i<=m;++i){
q.push_back(i-1);
ita=q.begin();
while(q.size()>1&&y[*(ita+1)]-y[*ita]<=1ll*data[i].limit*((*(ita+1))-(*ita))) q.pop_front(),ita=q.begin();
register int j=q.front();
dp[t][i]=min(dp[t][i],dp[t-1][j]+1ll*(i-j)*data[i].limit-(sumdata[i]-sumdata[j]));
ita=q.end()-1;
while(q.size()>1&&(y[*ita]-1ll*y[*(ita-1)])*(i-(*ita))>=1ll*(y[i]-y[*ita])*((*ita)-(*(ita-1)))) q.pop_back(),ita=q.end()-1;;
}
}
cout<<dp[p][m]<<endl;
return 0;
}