刷了n次用了奇淫技巧才拿到rk1,亥
這道題是網絡流二十四題中「餐巾計划問題」的加強版。
於是懷着試一試的心情用費用流交了一發:
哇塞,過了9個點!(強烈譴責出題人用*造數據
下面是費用流解法簡述:
那么我們把每一天拆為早上和晚上兩個點,設為 \(day_i,night_i\) 。
首先可以人為地規定一點:對一塊餐巾,我們要么在其臟了之后立即送洗,要么買一塊新干凈餐巾來代替它。
然后對於每一個操作,我們可以如下連邊:
對於買新干凈餐巾:我們可以視作從源點買餐巾,從 \(s\) 向 \(day_i\) 連邊即可
對於送慢洗部:從 \(night_i\) 向 \(day_{i+n}\) 連邊,流量為 \(inf\) ,費用為 \(f\)。
對於送快洗部:從 \(night_i\) 向 \(day_{i+m}\) 連邊,流量為 \(inf\) ,費用為 \(s\)。
但是我們注意到,可能存在某一天的干凈餐巾冗余。
於是我們要從 \(day_i\) 向 \(day_{i+1}\) 連一條費用為零,流量為 \(inf\) 的單向邊。
如何保證每天剛好只用 \(r_i\) 塊餐巾?
將其拆成兩個板塊:
從 \(day_i\) 向 \(t\) 連邊,流量為 \(r_i\),費用為 \(0\)。
從 \(s\) 向 \(night_i\) 連邊,流量為 \(r_i\),費用為 \(0\)。
優化無果,於是猜想可以不用費用流。
首先無論餐巾是最開始一起買還是需要用時再買,不會影響最終的答案。
那么如果我們已經確定了要買的新餐巾的張數,那么是否可以確定一種唯一的方案使得總花費最小呢?
不難得到有這樣一種貪心策略:
在能夠用新餐巾的時候,盡量使用新餐巾。
設 \(m\) 為慢洗的天數。
如果無新餐巾可用,則倒回到 \(m\) 天前,找用過的舊餐巾進行慢洗。
如果沒有 \(m\) 天以前的舊餐巾可用,則由時間線從近到遠地找舊餐巾進行快洗。
這樣做使得時間線較遠的舊餐巾更有可能慢洗。
如果慢洗比快洗貴,那么直接將慢洗的時間和價格都改為快洗的時間和價格即可。
朴素代碼無O2只能過掉70分。
於是我懷疑單次判斷的時間復雜度過高。有的題解里說是 \(O(n)\) 的,但我死活沒看出來。
於是我們可以優化常數。
注意到在如果在第 \(i\) 天需要慢洗,所有在第 \(i-m\) 天前的舊餐巾對於我們來說是等效的,因為我們顯然沒有辦法將其拿去快洗。
所以我們每次將第 \(i-m\) 天前的舊餐巾全部累加至第 \(i-m\) 天即可。這樣可以優化一定的常數。
這樣可以保證在計算慢洗的時候一定是線性的,但仍然不能保證計算快洗部分時為線性。
最后我們需要確定最優解時需購買餐巾的張數 \(c\)。
設最優解時最小代價為 \(f(c)\),設當前枚舉到了 \(x\)。
則當 $x \ge c $,一定有 \(f(x) \ge f(c)\),因為你還需要多出錢買新餐巾。
當 \(x \le c\),則要么不存在 \(f(x)\) ,即購買 \(x\) 張餐巾不能達到目的,要么此時多洗一張餐巾一定劣於多買一張餐巾,也有 \(f(x) \ge f(c)\)。
所以對於最優解 \(c\) 我們可以三分求解。
個人認為代碼可讀性還是挺高的,可以康康代碼:
/*---Author:HenryHuang---*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=5e5+5;
const ll inf=1ll<<50;
ll sumr=0;
ll r[maxn],res[maxn];
ll n,m1,m2,c1,c2,p;
ll f(ll num){
ll ans=1ll*num*p;//提前算好新餐巾代價
ll now=0;
for(ll i=1;i<=n;++i){
res[i]=0;
if(r[i]){
ll tmp=r[i];
if(num){
ll k=min(tmp,num);
tmp-=k,res[i]+=k,num-=k;
if(!tmp) continue;
}//直接用新的
for(ll j=now;j<i-m2;++j){
res[j+1]+=res[j],res[j]=0;
}//累加舊洗餐巾
now=max(now,i-m2);
if(now<i&&res[now]){
ll k=min(tmp,res[now]);
res[now]-=k,res[i]+=k;
tmp-=k,ans+=1ll*k*c2;
if(!tmp) continue;
}//慢洗
for(ll j=i-m1;j>=1&&j>i-m2;--j){
if(res[j]){
ll k=min(tmp,res[j]);
res[j]-=k,res[i]+=k;
tmp-=k,ans+=1ll*k*c1;
}
if(!tmp) break;
}//快洗
if(tmp) return inf;
}
}
return ans;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m1>>m2>>c1>>c2>>p;
if(m1>m2) swap(m1,m2),swap(c1,c2);//1快2慢
if(c1<c2) c2=c1,m2=m1;
for(ll i=1;i<=n;++i)
cin>>r[i],sumr+=r[i];
ll l=1,r=max(10000ll,sumr/3);//奇淫技巧
ll ans=inf;
while(r-l>2){
ll k=(r-l)/3;
ll mid1=l+k,mid2=r-k;
ll aa=f(mid1),bb=f(mid2);
if(aa<bb) ans=min(ans,aa),r=mid2;
else ans=min(ans,bb),l=mid1;
}
for(ll i=l;i<=r;++i) ans=min(ans,f(i));
cout<<ans<<'\n';
return 0;
}