樹DP 基環樹 NOI2013 快餐店

2021-07-14 13:33:45 字數 4065 閱讀 3266

小 t 打算在城市 c 開設一家外送快餐店。送餐到某乙個地點的時間與外賣店到該地點之間最短路徑長度是成正比的,小 t 希望快餐店的位址選在離最遠的顧客距離最近的地方。

快餐店的顧客分布在城市 c 的

n 個建築中,這

n個建築通過恰好

n 條雙向道路連線起來,不存在任何兩條道路連線了相同的兩個建築。任意兩個建築之間至少存在一條由雙向道路連線而成的路徑。小 t 的快餐店可以開設在任一建築中,也可以開設在任意一條道路的某個位置上(該位置與道路兩端的建築的距離不一定是整數)。

現給定城市 c 的地圖(道路分布及其長度),請找出最佳的快餐店選址,輸出其與最遠的顧客之間的距離。

第一行包含乙個正整數

n,表示城市 c 中的建築和道路數目。

接下來

n 行,每行

3個整數,ai

,bi,

li(1≤i

≤n;l_i > 0

),表明一條道路連線了建築 ai

與 bi

,其長度為 li

。包含乙個實數,四捨五入保留恰好一位小數,表示最佳快餐店選址距離最遠使用者的距離。

注意:你的結果必須恰好有一位小數,小數字數不正確不得分。

input

4

1 2 1

1 4 2

1 3 2

2 4 1

output
2.0
explanation

最優選址為建築

1 。到達

4個建築的距離分別為 0,

1,2,

2 。

input

5

1 5 100

2 1 77

3 2 80

4 1 64

5 3 41

output
109.0
explanation

最佳選址為

1 到

5這條邊上,距離

1 的距離為

32的位置。

對於 10% 的資料,n≤

80,li

=1。對於 30% 的資料,n≤

600,li

≤100

。對於 60% 的資料,n≤

2000,l

i≤109

。對於 100% 的資料,n≤

105,l

i≤109

時間限制:2s

空間限制:

512mb

首先對於一棵樹,顯然答案等於樹的直徑除以2,然後我們來考慮基環樹。

我們可以把基環樹看做乙個環+乙個森林,其中每棵樹的根就是這棵樹和環的公共點。

顯然環上總有一條邊一定不會被走到,我們列舉環上的邊,刪掉它,然後求直徑,時間複雜度o(

n2) ,妥妥的60分。

我們能不能快速求直徑呢?

我們考慮直徑有哪些情況:

在樹的內部

這個我們直接預處理,然後取乙個最大值,記作mx

,因為刪除環上的邊對於這種情況沒有影響。

經過了環上的邊

這種情況是我們重點需要解決的問題。

接下來,我們來討論這個問題:

注意,為方便表述,以下的編號為破環為鏈後陣列的下標

首先,破環為鏈(即將這個環的序列再複製一遍),成為乙個序列,每次刪邊之後就對應序列的乙個區間,我們令di

表示以i為根的子樹中深度最深的點的深度(即子樹中以i為乙個端點的最長鏈),di

sti,

j 標表示i,j兩個點之間的距離,那麼經過環的最長鏈就為di

+dis

ti,j

+dji

,j∈[

l,r]

l,r為當前對應的區間。 其實d

isti

,j可以用字首和來解決,在序列上求乙個邊權的字首和,在di

sti,

j=su

mj−s

umii

那麼最長鏈就為di

+sum

j−su

mi+d

j=di

−sum

i+dj

+sum

j ,我們用線段樹分別維護一下di

−sum

i 和di

+sum

i 的最大值即可。不過這兩個東西的最大值可能在同乙個位置出現,所以再維護一下次大值。每次對線段樹詢問對應區間即可。

這裡有乙個問題,假設di

−sum

i 和di

+sum

i 的最大值分別出現在

i ,

j,怎樣保證

i<

j 呢?

我們分類討論一下

有三個點i,

j,k ,

i<

j所以,我們可以放心地使用這種方法。時間複雜度o(

nlog

2n)

#include

#include

#include

using

namespace

std;

#define maxn 100000

#define inf 0x7fffffffffffffffll

typedef

long

long ll;

int n,bgc,cir[maxn*2+10],cnt;

bool vis[maxn+10];

ll ans=inf,f[maxn+10][3],mx,sum[maxn*2+10];

void read(int &x)

}struct node*adj[maxn+10],edge[maxn*2+10],*ecnt=edge,*pre[maxn+10];

inline

void addedge(int u,int v,int wt)

void read()

}void find_circle(int u)

vis[u]=1;

for(node *p=adj[u];p;p=p->next)

}}void dfs(int u,int fa)

else

if(f[p->v][0]+p->wt>f[u][1])

f[u][1]=f[p->v][0]+p->wt;

f[u][2]=max(f[u][2],f[p->v][2]);}}

f[u][2]=max(f[u][2],f[u][0]+f[u][1]);

}namespace segmenttree

inline node(ll mx,ll smx,int mxpos):mx(mx),smx(smx),mxpos(mxpos)

node *ch[2];

}tree[maxn*8+10],*root[2],*tcnt=tree;

void update(node *p)

else

}node merge_ans(const node &a,const node &b)

void build(node *&p,int l,int r,int ff)

int mid((l+r)>>1);

build(p->ch[0],l,mid,ff);

build(p->ch[1],mid+1,r,ff);

update(p);

}node get_ans(node *p,int l,int r,int ll,int rr)

}void solve()while(i!=bgc);

for(i=1;i<=cnt;i++)

for(i=2;i<=2*cnt;i++)

sum[i]=sum[i-1]+pre[cir[i-1]]->wt;

segmenttree::build(segmenttree::root[0],1,cnt*2,1);

segmenttree::build(segmenttree::root[1],1,cnt*2,-1);

segmenttree::node a,b;

for(i=1;i<=cnt;i++)

ans=max(ans,mx);

}int main()

NOI2013 快餐店 環套樹 線段樹

題目大意 給你一顆環套樹,你要在這棵的邊上 包括端點 找乙個點,使得離該點最遠的點最近。資料範圍 n 10 5 邊權 10 9 此題不難看出一種暴力做法,我們依次斷開環上的一條邊,然後求整顆樹的直徑,取個 min 就好了,時間複雜度是 o n 2 的。然而顯然會 t 我們考慮一些優秀的做法,我們首先...

UOJ126 NOI2013 快餐店(基環樹dp)

題目鏈結 思路 現在圖中保證一定只有乙個環,這個基環樹,也就是說去掉環上的任意一條邊,它能形成一棵樹,先看最長路徑不在環上的情況,那麼最長路徑就是在環上的點為根的子樹中了,這個求下樹的直徑即可。看在環上的,如果最長路徑在環上,最長路徑一定有一條環上的邊沒有經過,假設 u v u,v 是沒有經過地邊,...

動態規劃 NOI2013 快餐店

第一行包含乙個整數n,表示城市c中的建築和道路數目。接下來n行,每行3個整數,ai,bi,li 1 i n li 0 表示一條道路連線了建築ai與bi,其長度為li 僅包含乙個實數,四捨五入保留恰好一位小數,表示最佳快餐店選址距離最遠使用者的距離。注意 你的結果必須恰好有一位小數,小數字數不正確不得...