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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

【图论】【模板】静态仙人掌(luogu 5236)

發布時間:2023/12/3 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【图论】【模板】静态仙人掌(luogu 5236) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【模板】靜態仙人掌

題目大意

給你一個無向仙人掌圖(保證每條邊至多出現在一個簡單回路中的無向圖),問你兩個點之間的最短路距離

輸入樣例#1

9 10 2 1 2 1 1 4 1 3 4 1 2 3 1 3 7 1 7 8 2 7 9 2 1 5 3 1 6 4 5 6 1 1 9 5 7

輸出樣例#1

5 6

輸入樣例#2

9 10 3 1 2 1 2 3 1 2 4 4 3 4 2 4 5 1 5 6 1 6 7 2 7 8 2 8 9 4 5 9 2 1 9 5 8 3 4

輸出樣例#2

7 5 2

樣例解釋:

樣例1中的仙人掌是這個樣子的:

詢問有兩個,分別是詢問 1→\rightarrow 9和 5→\rightarrow 7的最短路
顯然答案分別為 5 和 6。

數據范圍:

1≤n,q≤100001\le n,q \le 100001n,q10000
1≤m≤200001\le m \le 200001m20000
1≤w≤1051\le w \le 10^51w105
請注意時限為 300ms300\text{ms}300ms

解題思路

我們把該圖轉化為圓方樹
建樹規則:
1:對于不在環里面的邊,我們保留不變
2:對于環,我們建一個方點(黃色的點),連接這個該環上所有點,邊權為為所有點到dfsdfsdfs序最小的點的距離(如圖)
搜索的時候,我們記錄下某個點的dfsdfsdfs序,以及從這個點出發走到的點中dfsdfsdfs序最小的點的dfsdfsdfs序(父親邊除外)
當我們搜到某個點時,枚舉到某條邊(如圖紅色的邊),若該邊指向的點已經搜過,且父親節點不是該點,且dfs序大于該點,那么我們搜到了一個環,且這個環中dfsdfsdfs序最小的點就是該點
dfsdfsdfs序大于該點,很顯然是該點出發搜索到的點
且與該點相連,那么肯定是一個環了
從該點出發,一邊是從該點搜索到的點,且dfs序逐漸變大,dfsdfsdfs序大于該點,另一邊是紅色邊的點,dfsdfsdfs序也大于該點
那么該點就是該環所有點中dfsdfsdfs序最小的點

我們像這樣建樹
然后得到了一棵圓方數
對于樹上兩點的最短距離
就是兩點到lcalcalca的距離
lcalcalca不是方點,但經過方點,那經過該環的距離就是走到dfsdfsdfs序最小的點最短的距離,也就是從該環中某個圓點到方點再到dfsdfsdfs序最小的點的距離,所以沒有影響

對于lcalcalca是方點的,我們先計算到該環某個圓點的距離,然后求到對方點的最小距離即可(就是兩個方向距離的minminmin

代碼

#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; ll n, m, w, x, y, z, q, X, Y, ex, ans, tot, tott, b[100010], fa[200010], dep[200010], dis[200010], sum[200010], dfn[200010], low[200010], h[200010], head[200010], f[200010][20]; struct rec {ll to, l, next; }e[2000010], a[2000010]; int read()//快讀 {char x=getchar();int d=1,l=0;while (x<'0'||x>'9') {if (x=='-') d=-1;x=getchar();}while (x>='0'&&x<='9') l=(l<<3)+(l<<1)+x-48,x=getchar();return l*d; } void writ(int c) {if (c>9) writ(c/10); putchar(c%10+48); return;} void write(int s) {s<0?putchar(45),writ(-s):writ(s); putchar(10); return;} void add(ll x, ll y, ll z)//加圓方樹的邊 {a[++tot].to = y;a[tot].l = z;a[tot].next = head[x];head[x] = tot;a[++tot].to = x;a[tot].l = z;a[tot].next = head[y];head[y] = tot; } void addd(ll x, ll y, ll z)//加原圖的邊 {e[++tott].to = y;e[tott].l = z;e[tott].next = h[x];h[x] = tott;e[++tott].to = x;e[tott].l = z;e[tott].next = h[y];h[y] = tott; } void jh(ll x, ll y, ll z)//對于環,建圓方樹 {++ex;ll pt = y, ss = z;while(pt != fa[x])//求從x到所有點走反邊的距離(紅色邊的方向){sum[pt] = ss;ss += b[pt];//求和pt = fa[pt];}sum[ex] = sum[x];sum[x] = 0;pt = y;ss = 0;while(pt != fa[x]){ss = min(sum[pt], sum[ex] - sum[pt]);//走兩條邊中最短的add(pt, ex, ss);//加邊pt = fa[pt];} } void dfs(ll x) {dfn[x] = low[x] = ++w;for (int i = h[x]; i; i = e[i].next)if (e[i].to != fa[x]) {ll v = e[i].to;if (!dfn[v])//沒走過{fa[v] = x;b[v] = e[i].l;//記錄dfs(v);low[x] = min(low[x], low[v]);//記錄由該點走出去的點中dfs序最小的}else low[x] = min(low[x], dfn[v]);//到過了,要不就是走回了變if (low[v] > dfn[x]) add(x, v, e[i].l);}for (int i = h[x]; i; i = e[i].next)if ( dfn[e[i].to] > dfn[x] && fa[e[i].to] != x)jh(x, e[i].to, e[i].l); } void dfs1(int x) {dep[x] = dep[f[x][0]] + 1;for (int j = 1; j <= 16; ++j)f[x][j] = f[f[x][j - 1]][j - 1];//倍增for (int i = head[x]; i; i = a[i].next)if (a[i].to != f[x][0]){f[a[i].to][0] = x;if (dis[a[i].to]) dis[a[i].to] = min(dis[a[i].to], dis[x] + a[i].l);else dis[a[i].to] = dis[x] + a[i].l;dfs1(a[i].to);} } ll lca(ll x, ll y) {if (dep[x] < dep[y]) swap(x, y);//求lcafor (int i = 16; i >= 0; --i)if (dep[f[x][i]] >= dep[y]) x = f[x][i];for (int i = 16; i >= 0; --i)if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];X = x;//若兩點不是祖先關系,那會停留在前一個點Y = y;return x == y?x:f[x][0]; } int main() {n = read();m = read();q = read();ex = n;for (int i = 1; i <= m; ++i){x = read();y = read();z = read();addd(x, y, z);}dfs(1);f[1][0] = 1;dfs1(1);for (int i = 1; i <= q; ++i){x = read();y = read();z = lca(x, y);if (z <= n) ans = dis[x] + dis[y] - dis[z] - dis[z];//lca是圓點else{ans = dis[x] - dis[X] + dis[y] - dis[Y]; //到環上兩圓點的距離if (sum[X] > sum[Y]) swap(X, Y);ans += min(sum[Y] - sum[X], sum[z] - sum[Y] + sum[X]);//環的兩個方向}write(ans);}return 0; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的【图论】【模板】静态仙人掌(luogu 5236)的全部內容,希望文章能夠幫你解決所遇到的問題。

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