NOI2014 購票(樹形dp 樹剖 斜率優化)

2021-10-08 06:53:35 字數 3863 閱讀 5920

考慮樹形 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

log⁡n)

o(n\log n)

o(nlogn)

,因為每一層只有 n

nn 條直線,一共有 log⁡n

\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 ...