考慮樹形 dp,設 dpi
dp_i
dpi
為 ii
i 節點到 sz 市的最小費用,dis
idis_i
disi
為 sz 市到 i
ii 節點的距離。
顯然初始化 dp1
=0
dp_1=0
dp1=0
,然後 dis
idis_i
disi
可以提前預處理出來。
然後有 dpu
=min(
dpv+
(dis
u−di
sv)×
pu+q
u)
dp_u=\min(dp_v+(dis_u-dis_v)\times p_u+q_u)
dpu
=min(d
pv+
(dis
u−d
isv
)×pu
+qu
)。(v
vv 為 u
uu 的祖先,dis
u−di
sv≤l
udis_u-dis_v\leq l_u
disu−
disv
≤lu
)。拆開來:dpu
=dis
u×pu
+qu+
min(−
disv
×pu+
dpv)
dp_u=dis_u\times p_u+q_u+\min(-dis_v\times p_u+dp_v)
dpu=d
isu
×pu
+qu
+min(−
disv
×pu
+dp
v)。
發現拆出來跟 v
vv 有關係的只有 −di
sv×p
u+dp
v-dis_v\times p_u+dp_v
−disv
×pu
+dpv
。然後看到這個東西就聯想到斜率優化。
那麼思路就顯而易見了:先對原樹進行樹剖,然後對於每個點 i
ii 維護一條直線 fi(
x)=−
disi
×x+d
pi
f_i(x)=-dis_i\times x+dp_i
fi(x)
=−di
si×
x+dp
i。至於如何維護這些直線,我們可以在每個線段樹節點開乙個vector
,那麼可以證明線段樹總的空間複雜度是 o(n
logn)
o(n\log n)
o(nlogn)
,因為每一層只有 n
nn 條直線,一共有 logn
\log n
logn
層。然後要不斷插入一些直線,再用類似半平面交的方法維護下凸殼。
至於 l
ll 的限制,我們可以先用倍增求出來每乙個點最遠能跳到哪個點。
**如下:(有詳細注釋)
#include
#define ln 18
#define n 200010
#define ll long long
#define inf 0x7fffffffffffffff
using
namespace std;
struct point
;point
(double xx,
double yy)};
struct line
;line
(ll kk,ll bb)
ll get
(ll x)};
point intersection
(line a,line b)
//兩直線求交點
bool
onright
(point a,line l)
//判斷乙個點是否在這條直線的上方
int n,subtask;
int cnt,head[n]
,nxt[n]
,to[n]
;int tot,size[n]
,d[n]
,son[n]
,id[n]
,rk[n]
,top[n]
,farest[n]
;int fa[n]
[ln]
;ll p[n]
,q[n]
,limit[n]
,dis[n]
,w[n]
,dp[n]
,sum[n]
[ln]
;//有很多地方要開long long,注意
vectorline[n<<2]
;void
adde
(int u,
int v,ll wi)
void
dfs(
int u)
int now=u;
for(
int i=
17;i>=
0;i--
)//倍增預處理最遠能跳到哪個點
} farest[u]
=now;
for(
int i=head[u]
;i;i=nxt[i])}
void
dfs1
(int u,
int tp)
void
half
(int k,line tmp)
//插入新直線
line[k]
.push_back
(tmp);}
void
insert
(int k,
int l,
int r,
int qx,line tmp)
ll query
(int k,
int l,
int r,
int ql,
int qr,ll x)
if(xelse l=mid+1;
}return line[k]
[ans]
.get
(x);
//將x代入得到y值
}int mid=
(l+r)
>>1;
ll ans=inf;
if(ql<=mid) ans=
min(ans,
query
(k<<
1,l,mid,ql,qr,x));
if(qr>mid) ans=
min(ans,
query
(k<<1|
1,mid+
1,r,ql,qr,x));
return ans;
}void
solve
(int u)
ans=
min(ans,
query(1
,1,n,id[b]
,id[a]
,p[v]))
; dp[v]
=ans+dis[v]
*p[v]
+q[v]
;solve
(v);}}
intmain()
dfs(1)
;dfs1(1
,1);
solve(1
);for(
int i=
2;i<=n;i++
)printf
("%lld\n"
,dp[i]);
return0;
}
NOI2014 購票(樹形dp 樹剖 斜率優化)
考慮樹形 dp,設 dpi dp i dpi 為 ii i 節點到 sz 市的最小費用,dis idis i disi 為 sz 市到 i ii 節點的距離。顯然初始化 dp1 0 dp 1 0 dp1 0 然後 dis idis i disi 可以提前預處理出來。然後有 dpu min dpv d...
題解 NOI2014購票
我們首先get出斜率優化方程 dp v dis v p u dis u p u q u dp u left 0 dis u dis v lim u right 那麼 y dp v x dis v k p u 我們所要做的就是維護乙個下凸包以維護最小的b值。現在有兩個本題的難點 首先,轉移到 u 點的...
NOI2014 購票 線段樹做法
先推出動態轉移方程方程,它有乙個距離限制,這一維可以用線段樹維護,然後線段樹中的凸包要求有回溯操作,這個每次只會修改top和那一為的值,記錄一下即可。bzoj上tle了。include include include include include define rep i,x,y for int ...