【LuoguP3994】高速公路
阿新 • • 發佈:2018-12-10
題目描述
C國擁有一張四通八達的高速公路網樹,其中有n個城市,城市之間由一共n-1條高速公路連線。除了首都1號城市,每個城市都有一家本地的客運公司,可以發車前往全國各地,有若干條高速公路連向其他城市,這是一個樹型結構,1號城市(首都)為根。假設有一個人要從i號城市坐車出發前往j號城市,那麼他要花費Pi*(i城市到j城市的距離)+Qi元。由於距離首都越遠,國家的監管就越鬆,所以距離首都越遠,客運公司的Pi(單位距離價格)越大,形式化的說,如果把高速路網看成一棵以首都為根的有根樹,i號城市是j號城市的某個祖先,那麼一定存在Pi<=Pj。
Sol
假裝我們只用考慮從祖先轉移過來
那麼化下式子就是:
裡面就是個裸的斜率優化了 然而顯然直接做是不行的,因為這是一顆樹
那麼就需要對斜率優化有更深刻的理解才能做了
一種暴力的做法是用可持久化線段樹來維護單調佇列的陣列
但是主意到我們每次考慮一個元素時只是更改了首尾的指標,並且還修改了至多佇列中的一個元素(被當前元素覆蓋了),那麼我們可以直接暴力撤銷操作,獲得滿分
但其實這樣做複雜度是不對的,因為這樣你會發現一個元素不僅僅是入隊出隊了一次,而是可能被反覆加進來,這樣就會被卡掉
因為斜率優化的實質是維護了一個凸包,也就是說裡面直線的斜率是單調的,那麼我們可以不用暴力出對,而改成二分這個位置然後一次性出隊,這樣複雜度就變成了穩定的
(然而能直接過有什麼不好的)
暴力出隊:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cmath>
#include<set>
using namespace std;
inline int read()
{
int x=0;char ch=getchar();int t=1;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-' )t=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
return x*t;
}
const int N=1e6+10;
struct edge{
int to,next;
}a[N];
int cur[N];int cnt=0;
inline void add(int x,int y){a[++cnt]=(edge){y,cur[x]};cur[x]=cnt;}
typedef long long ll;
int P[N];
int Q[N];
int fa[N],W[N];
int n;
int que[N];int h,t;
ll dp[N],dis[N];
int head[N],tail[N],pos[N],pre[N];
typedef double db;
inline db getk(int p,int q){
return (db)(dp[p]-dp[q])/(db)(dis[p]-dis[q]);
}
void dfs(int u)
{
head[u]=h;tail[u]=t;
while(h<t&&getk(que[h],que[h+1])<=1.00*P[u]) ++h;
dp[u]=dp[que[h]]+(dis[u]-dis[que[h]])*P[u]+Q[u];
while(h<t&&getk(que[t],que[t-1])>getk(u,que[t])) --t;
pre[u]=que[++t];que[t]=u;pos[u]=t;
for(register int v,i=cur[u];i;i=a[i].next){
v=a[i].to;dis[v]=dis[u]+W[v];dfs(v);
}
h=head[u];t=tail[u];que[pos[u]]=pre[u];
return;
}
int main()
{
n=read();
for(register int i=2;i<=n;++i) fa[i]=read(),W[i]=read(),P[i]=read(),Q[i]=read(),add(fa[i],i);
h=1;t=0;
dfs(1);
for(register int i=2;i<=n;++i) printf("%lld\n",dp[i]);
return 0;
}