題解 P1967 貨車運輸

2022-05-05 20:15:11 字數 2991 閱讀 1722

貌似題解裡沒有樹鏈剖分和線段樹的,貢獻一發。

首先明確題目要求:一輛車走某條路從x城到y城的邊權最小值

我們把要求分開來看:

從x城到y城:我們需要走的路徑將兩點聯通

邊權最小值:我們要找這條路上的限重最小值

如果你是乙個貨車司機(而且題目還告訴你你的汽車走多遠不要油),你肯定想多運一些貨物,也就要求聯通兩點的權值盡可能大。

又要保證聯通,又要保證權值盡可能大,沒錯,我們需要用到最小生成樹。

(如果還不理解,你可以設想一下,有兩條都可以從a到b,一條路限重10,一條路限重100,你一定會選擇第二條路;我們再推廣一下,如果兩條路都能聯通還未聯通的a、b兩個聯通塊(你可以認為a、b是兩個島,兩條路是跨島大橋),一條路限重10,一條路限重100,你還是一定會選擇第二條路)

最小生成樹的方法:先按邊權大小排序,利用並查集判斷兩塊是否聯通,生成乙個新的圖

好,現在第乙個問題解決了:你運貨的最大路徑方案一定在新的圖(樹)上了,怎麼求兩點之間權值最小的呢?

因為這是一棵樹,所以兩點之間路徑唯一,可是直接搜尋時間又肯定承受不住,我們這時就可以採用樹鏈剖分了

這是類似樹剖板題的題,就有提到求某兩點的最值問題

值得一提的是:樹剖+線段樹只是支援修改和查詢點權的,這時我們就需要知道怎麼將邊權轉換為點權

隨便在網上找了個圖:我們這樣實現邊權與點權之間的轉換:將根節點的點權設為inf,然後所有邊權下放到連線的點(所有邊權往下挪到了點裡,由於根節點值為inf不影響min的計算(同理,查詢最大值就設為-inf))

然後直接查詢就好啦!

怎麼可能?!

剛開始的時候,我轉換完後就直接像樹剖板題那樣求最值了,結果只有10分,那麼問題出在哪呢?

我們看一下這個圖(黑色是邊權,黃色是轉換後的點權):

若想查詢a點到b點的最值,我們會發現,按普通樹剖的查詢方法,我們會訪問20那個點(5-20-19-8),然而應該訪問的路徑是5-19-8,所以我們要對查詢函式做一些修改,「繞開那些點」

void getans(int x,int y)

int ans = inf;

while(top[x] != top[y])

if(x == y)

if(dep[x] > dep[y])swap(x,y);

ans = min(ans,query(1,pos[x] + 1,pos[y]));//+1繞開

printf("%d\n",ans);

}

#include#include#include#include#include#includeusing namespace std;

int rd()

while(c >= '0' && c <= '9')

return flag * out;

}const int maxn = 500190,inf = 999999999;

int num,nr,nume,na,cnt,numt;

int head[maxn];

struct nodee[maxn * 2];

void add(int u,int v,int dis)

struct ri[maxn];

bool cmp(r a,r b)

int father[maxn];

int findfather(int v)

void union(int a,int b)

void buildg()}}

int dep[maxn],fa[maxn],wson[maxn],top[maxn],size[maxn],pos[maxn],ori[maxn];

int val[maxn];

int vis[maxn];

void dfs1(int id,int f)}}

void dfs2(int id,int tp)

}#define lid (id << 1)

#define rid (id << 1) | 1

struct sag_treetree[maxn << 2];

void build(int id,int l,int r)

int mid = l + r >> 1;

build(lid,l,mid);

build(rid,mid + 1,r);

tree[id].min = min(tree[lid].min,tree[rid].min);

}int query(int id,int l,int r)

int mid = tree[id].l + tree[id].r >> 1;

if(mid < l)

else if(mid >= r)

else

}void getans(int x,int y)

int ans = inf;

while(top[x] != top[y])

if(x == y)

if(dep[x] > dep[y])swap(x,y);

ans = min(ans,query(1,pos[x] + 1,pos[y]));

printf("%d\n",ans);

}int main()

for(int i = 1;i <= nr;i++)

sort(i + 1,i + 1 + nr,cmp);

buildg();

int s = 1;

while(s <= num)

s++;

}build(1,1,numt);

na = rd();

int u,v;

for(int i = 1;i <= na;i++)

return 0;

}

最後,感謝大佬的幫助

大佬廣告

P1967 貨車運輸

題目 題目描述 aa國有n n座城市,編號從 1 1到 nn,城市之間有 mm 條雙向道路。每一條道路對車輛都有重量限制,簡稱限重。現在有 qq 輛貨車在運輸貨物,司機們想知道每輛車在不超過車輛限重的情況下,最多能運多重的貨物。輸入格式 第一行有兩個用乙個空格隔開的整數 n,mn,m,表示 aa 國...

P1967 貨車運輸

a 國有 n 座城市,編號從 1 到 n 城市之間有 m 條雙向道路。每一條道路對車輛都有重量限制,簡稱限重。現在有 q 輛貨車在運輸貨物,司機們想知道每輛車在不超過車輛限重的情況下,最多能運多重的貨物。第一行有兩個用乙個空格隔開的整數 n m 表示 a 國有 n 座城市和 m 條道路。接下來 m ...

P1967 貨車運輸

a 國有 n 座城市,編號從 1 到 n,城市之間有 m 條雙向道路。每一條道路對車輛都有重量限制,簡稱限重。現在有 q 輛貨車在運輸貨物,司機們想知道每輛車在不超過車輛限重的情況下,最多能運多重的貨物。第一行有兩個用乙個空格隔開的整數 n,m,表示 a 國有 n 座城市和 m 條道 路。接下來 m...