[CF1101F]Trucks and Cities
阿新 • • 發佈:2020-07-29
題目
題解
對於這種求最小值的題,我們顯然有一種二分的做法,先用 \(\log\) 的複雜度二分一個油箱的大小,再對其進行合法性檢查,這樣做時間複雜度 \(\mathcal O(nm\log 10^{18})\)
顯然,這樣做似乎要超時,雖然時間複雜度最差情況下就差一點就可以卡過去,但是這個方法好像無法進行優化了,如果要得到部分分,這個方法是很好的
考慮換個思路,對於一輛車,有 \(s_i,t_i,c_i,r_i\),其實就是將 \([s_i,t_i]\) 劃分成 \(r_i+1\) 個區間,取最大的區間 \(\max l_i\) 再將其乘上 \(c_i\),然後對於 \(m\) 輛車取 \(\max\{c_i\times max l_i\}\)
現在問題是怎麼得到 \(\max l_i\)?
考慮區間 \(DP\),定義 \(f[i][j][k]\) 為將 \([i,j]\) 劃分成 \(k\) 個區間,使得這 \(k\) 個區間的最大值最小的值,顯然有轉移
\[f[i][j][k]=\min\{\max\{f[i][l][k-1],a[j]-a[l]|l\in [i,j]\}\} \]
狀態 \(n^3\),轉移 \(\mathcal O(n)\),那麼總複雜度為 \(\mathcal O(n^4)\),對於 \(n\le 400\) 的資料,這有點困難
但是,注意到 \(\max\) 中的右邊 \(a[j]-a[l]\),對於同一個 \(l\)
程式碼
#include<cstdio> #include<algorithm> #include<cstring> #include<vector> using namespace std; const int MAXN=400; const int MAXM=250000; const int INF=0x3f3f3f3f; int a[MAXN+5]; int s[MAXM+5],t[MAXM+5],c[MAXM+5],r[MAXM+5]; vector<int>id[MAXN+5]; int n,m; inline void Init(){ scanf("%d %d",&n,&m); for(int i=1;i<=n;++i)scanf("%d",&a[i]); for(int i=1;i<=m;++i){ scanf("%d %d %d %d",&s[i],&t[i],&c[i],&r[i]),++r[i]; r[i]=min(r[i],t[i]-s[i]); id[r[i]].push_back(i); } } int f[MAXN+5][MAXN+5][2],now=0; inline void Getdp(){ for(int i=1;i<=n;++i)for(int j=i;j<=n;++j) f[i][j][now]=a[j]-a[i]; long long ans=-INF; for(auto i:id[1])ans=max(ans,1ll*f[s[i]][t[i]][0]*c[i]); for(int k=2;k<=n;++k){ now^=1; for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)f[i][j][now]=INF; for(int i=1;i<=n;++i) for(int j=i,l=i;j<=n;++j){ while(f[i][l][now^1]<a[j]-a[l])++l; f[i][j][now]=min(f[i][l][now^1],a[j]-a[l-1]); } for(auto i:id[k])ans=max(ans,1ll*f[s[i]][t[i]][now]*c[i]); } printf("%lld\n",ans); } signed main(){ Init(); Getdp(); return 0; } /* 10 10 2 3 4 8 9 10 12 13 15 19 3 8 3 1 3 4 3 2 1 9 2 1 1 9 3 1 6 10 2 1 3 9 2 0 3 7 2 1 2 3 3 0 3 9 2 0 4 10 3 0 output==21 ans==33 */