日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[TJOI2017]城市(未解决)

發布時間:2023/12/3 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [TJOI2017]城市(未解决) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

[TJOI2017]城市

題意:

一棵樹,現在要求你將一條邊改變他的位置,(即改變左右所連接的端點,權值不變),修改后任意兩點相互可達,且使得兩個點之間的最大交通費用最小

題解:

有O(n^2)和O(n)兩種做法


參考題解:
參考1
參考2
再有研究分析半小時后,我會了O(n^2)的做法:
我們可以暴力刪除一個邊,O(n)
然后處理最遠點對的問題O(n)
這個最遠點對怎么考慮?
當刪除一個邊后,整棵樹就被分成兩個聯通塊,此時的最長路由三種來源:

  • 最遠點對都在連通塊1
  • 最遠點對都在連通塊2
  • 最遠點對分別在連通塊1和2
  • 前兩種情況本質就是分別求樹的直徑,拿第三種情況呢?我們設最遠點對分別是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]城市(未解决)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。