[TJOI2017]城市(未解决)
[TJOI2017]城市
題意:
一棵樹,現在要求你將一條邊改變他的位置,(即改變左右所連接的端點,權值不變),修改后任意兩點相互可達,且使得兩個點之間的最大交通費用最小
題解:
有O(n^2)和O(n)兩種做法
參考題解:
參考1
參考2
再有研究分析半小時后,我會了O(n^2)的做法:
我們可以暴力刪除一個邊,O(n)
然后處理最遠點對的問題O(n)
這個最遠點對怎么考慮?
當刪除一個邊后,整棵樹就被分成兩個聯通塊,此時的最長路由三種來源:
前兩種情況本質就是分別求樹的直徑,拿第三種情況呢?我們設最遠點對分別是u,v
u在連通塊1,v在2
那么答案就是u到連通塊1任意一點的最長距離+val(當初被刪的邊的邊權)+v在連通塊2任意一點的最長距離
最終答案是要求這三種情況的最大值盡可能小,而前兩種情況是固定的(在刪完邊之后就是固定的),所有我們就要讓第三種情況中兩個點的到自己聯通塊的最長距離最小。這就是最優策略
具體做法:
我們一邊做樹形dp,一邊求出對于每一個子樹,求出經過它的最長鏈和次長鏈。直徑就是枚舉每個點,求max(最長鏈+次長鏈)
而我們要求點x在連通塊內的最遠距離(另一端為y),
y有可能在x的子樹內,那len(x,y)就是上面求得最長鏈。
y如果在子樹外,len(x,y)就等于x到父親fa的距離+from
from有兩個來源:
1.在父親子樹之外的最長鏈
2.在父親子樹之內的最長鏈
對于第2種情況,如果x在就在父親子樹的最長鏈上,那么我們就要取次長鏈,否則就是最長鏈
?這句話什么意思?我們是通過換根操作,用父親子樹的最長鏈來求x的最長鏈,如果x在父親子樹的最長鏈,換根就會影響最長鏈,所以要用次長鏈
通過下圖理解理解
代碼:
#include<cstdio> #include<algorithm> using namespace std; const int N=5010;const int INF=0x3f3f3f3f; struct data{int v;int nxt;int val;}edge[2*N]; int alist[N];int cnt;int n; inline void add(int u,int v,int val) {edge[++cnt].v=v;edge[cnt].nxt=alist[u];alist[u]=cnt;edge[cnt].val=val;} int dp[N];int mv[N];int nxdp[N];bool book[N];int rad=INF;int dis;int res=INF; void getd(int x)//求直徑 {book[x]=true;int nxt=alist[x];while(nxt){int v=edge[nxt].v;int val=edge[nxt].val;if(!book[v]){getd(v);int va=dp[v]+val;//最長鏈和次長鏈 if(va>dp[x]){//如果大于最大的 nxdp[x]=dp[x];dp[x]=va;mv[x]=v;//在最長鏈上,x的下一位是v }else if(va>nxdp[x]){//大于第二大 nxdp[x]=va;}}nxt=edge[nxt].nxt;}dis=max(dis,dp[x]+nxdp[x]);//更新直徑 } void getr(int x,int fr) {rad=min(rad,max(fr,dp[x]));//更新半徑 book[x]=false;int nxt=alist[x];while(nxt){int v=edge[nxt].v;int val=edge[nxt].val;if(book[v]){if(v==mv[x]){//如果在最長鏈 getr(v,max(nxdp[x]+val,fr+val));// nxdp[x]+val 就是次長鏈+val //fr 他父親的from }else { getr(v,max(dp[x]+val,fr+val));//否則就是最長鏈+val }}nxt=edge[nxt].nxt;} } inline void clear(){//清空dp的函數 for(int i=1;i<=n;i++){dp[i]=mv[i]=nxdp[i]=book[i]=0;}rad=INF;dis=0; } int u[N];int v[N];int val[N]; int main() {scanf("%d",&n);for(int i=1;i<n;i++)//讀進來 {scanf("%d%d%d",&u[i],&v[i],&val[i]);add(u[i],v[i],val[i]);add(v[i],u[i],val[i]);}for(int i=1;i<n;i++)//枚舉邊 {int d1;int d2;int r1;int r2;book[v[i]]=1;getd(u[i]);d1=dis;//求直徑 dis=0;getd(v[i]);d2=dis;//求直徑 book[v[i]]=0;getr(u[i],0);r1=rad;//求半徑 rad=INF;getr(v[i],0);r2=rad;//求半徑 res=min(res,max(max(d1,d2),r1+r2+val[i]));//更新答案 clear();//不要忘記清空 }printf("%d",res);return 0;//拜拜程序~ }總結
以上是生活随笔為你收集整理的[TJOI2017]城市(未解决)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 病毒性角膜炎如何治疗
- 下一篇: B - Labyrinth Gym -