仙人掌 && 圓方樹 || 靜態 + 動態 (差動態)

動態圓方樹(LCC)已棄療 四月也應該要退役了 是OI的謊言

大半天沒有一個正經點的教程的 不過這也不是個正經東西 比較冷門

那啥 貓某的仙人掌的課件放這了 提取碼: 8gtq 裏面講的很清楚了 這裏還有一個

好了 相信大家都懂了

第一階段(仙人掌圖)

初識仙人掌 主要根據定義亂搞

例如說 這道 題目 (網址不同)

如果 dp 的話 首先考慮樹上最長距離怎麼求

方法一:樹形 dp 使 f [p][0/1] 表示到 p 點往下能走的最長距離和第二長的距離 然後枚舉

然而並沒有什麼用 如果最長的路和第二長的路 是經過了同一仙人掌的兩條不同下去再合起來的

0ms 就 GG 了........

方法二:隨便一點遍歷到與它距離最遠的點 再從該點遍歷到離該點最遠的點

但是圖上行不通!例如這個圖 (專門 YY 了個很漂漂的仙人掌出來)

花了我好久搞的圖片

於是你會驚奇地發現 如果我們從 天藍色 的點 開始搜

你會跑到 粉粉 的點 那裏

然後再跑到三個綠色的點之一 得出直徑爲 9

然而事實上直徑應該是 兩個 淺綠色 的點 長度爲 10

 

可以看出 仙人掌這種東西着實毒瘤

 

方法三:針對仙人掌的環對其進行轟殺

嘛,不就出現了環麼 那我們處處針對它

考慮改進方法一 方法二的話就算了,難道要每個仙人掌枚舉刪邊嗎 =-= 極端情況全是三點仙人掌 複雜度 O(3^{\frac{n}{3}}\cdot n) 算了

(話說我分析的複雜度是對的嗎)

於是就開始像 樹形 dp 一樣 然後環的話 我開始用的是 裁半 的方法更新下去 像這樣 結果.......

inline void update1(int p)
{
	for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
		if (!is[b]) f[p] = max(f[p],f[b] + 1);
}
inline void update2(int p)
{
	ans1 = ans2 = 0;
	for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
		if (!is[b]) update(f[b]);
	ans = max(ans,ans1 + ans2 + 1);
}
void getf(int p)
{
	stack[++t] = p;
	o[p] = 1;
	for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
	if (dfn[b] > dfn[p] && !o[b]) getf(b);
	if (dfn[p] == low[p])
	if (stack[t] == p)
	{
		--t;
		for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
			if (dfn[b] > dfn[p]) f[p] = max(f[p],f[b] + 1);
	} else {
		memset(is,0,sizeof(is));
		int len = 0;
		while (stack[t] != p)
		cir[++len] = stack[t--],is[cir[len]] = 1; --t,is[p] = 1;
		update1(cir[(len >> 1) + 1]);
		update1(cir[len >> 1]);
		for (int i = (len >> 1) + 2,j = cir[i] ; i <= len ; ++ i,j = cir[i])
		for (int a = first[j],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
		{
			if (is[b] && dfn[b] < dfn[j]) continue;
			f[j] = max(f[j],f[b] + 1);
		}
		for (int i = ((len + 1) >> 1) - 1,j = cir[i] ; i > 0 ; -- i,j = cir[i])
		for (int a = first[j],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
		{
			if (is[b] && dfn[b] < dfn[j]) continue;
			f[j] = max(f[j],f[b] + 1);
		}
		for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
		{
			if (dfn[b] < dfn[p]) continue;
			f[p] = max(f[p],f[b] + 1);
		}
	}
}
void getdp(int p)
{
	stack[++t] = p;
	o[p] = 1;
	for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
		if (dfn[b] > dfn[p] && !o[b]) getdp(b);
	if (dfn[p] == low[p])
	if (stack[t] == p)
	{
		--tot,ans1 = ans2 = 0;
		for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
			if (dfn[b] > dfn[p]) update(f[b]);
		ans = max(ans,ans1 + ans2 + 1);
	} else {
		memset(is,0,sizeof(is));
		int len = 0;
		while (stack[t] != p)
		cir[++len] = stack[t--],is[cir[len]] = 1; --t,is[p] = 1;
		update2(cir[(len >> 1) + 1]);
		update2(cir[len >> 1]);
		for (int i = (len >> 1) + 2,j = cir[i] ; i <= len ; ++ i,j = cir[i])
		{
			ans1 = ans2 = 0;
			for (int a = first[j],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
			{
				if (is[b] && dfn[b] < dfn[j]) continue;
				update(f[b]);
			}
			ans = max(ans,ans1 + ans2 + 1);
		}
		for (int i = ((len + 1) >> 1) - 1,j = cir[i] ; i > 0 ; -- i,j = cir[i])
		{
			ans1 = ans2 = 0;
			for (int a = first[j],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
			{
				if (is[b] && dfn[b] < dfn[j]) continue;
				update(f[b]);
			}
			ans = max(ans,ans1 + ans2 + 1);
		}
		ans1 = ans2 = 0;
		for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
			if (dfn[b] > dfn[p]) update(f[b]);
		ans = max(ans,ans1 + ans2 + 1);
	}
}

於是 因爲一些毒瘤的路徑重複原因 調了兩個 20 節點的數據 過程都炸掉了

但居然還有 30 分 我的 4.4K Bytes 果然還是沒有白打的

然後頹了好幾個月沒搞 再回來的時候已經崩潰了 (題目的原因加上生活中的原因)

當然我知道上面幾行 和 我的 l j 代碼 諸君是不想看的

轉戰 正經 dp 1.7K Bytes 諸君放心

 

 

首先我們深知這種圖遍歷時 不像普通圖一樣可以到處亂撞

如果它在環裏 它不會跑到另一個環裏遍歷兩個及以上的點 可以理解爲不相關

如果不在環裏 就像樹一樣 搜到頭就回來了

因此我們使用無向圖 Tarjan 即可以充分判斷

話說無向圖 Tarjan 只需要在有向圖的程序裏面加上 判斷父親 即可 如果有需要就用數組 沒有就放子程序裏

然後爲了方便 我們順便在遍歷完圖後 從最下面開始更新答案 像這樣

void tarjan(int p) {
	dfn[p] = low[p] = ++tot;
	for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
	if (b != fa[p]) {
		!dfn[b] ? fa[b] = p,dep[b] = dep[p] + 1,tarjan(b),low[p] = min(low[p],low[b])
				: low[p] = min(low[p],dfn[b]); //經典的Tarjan不解釋了 dep是找環長用的
		if (low[b] > dfn[p]) ans = max(ans,dp[p] + dp[b] + 1),dp[p] = max(dp[p],dp[b] + 1);
	} b的連通分量的根大於p的搜索順序 說明不在同一環中 照常更新
	for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
		if (fa[b] != p && dfn[p] < dfn[b]) getcir(p,b); //這裏搜到環了就直接搞環
} //從下往上更新答案 無後效性

處理環的話就是一次性把一整個環都搞出來 在這之前我們必然保證 dfn 比該環大的點 都已經更新完了 因爲是 dfs

無後效性嘛 好了 那我們泰然自蒻地進入處理毒瘤環的環節

邊長必定爲 1 這個很好辦了

爲了不每個點繞一圈更新答案 我們考慮單調隊列優化

因爲最優的答案更新肯定小於半個環 我們不看環下面連的點 我們走環到環上點肯定是一半就能到兩邊了 所以把環斷了再複製上一倍 每次更新半個環

子程序裏面就只有複製環和單調隊列了 隊列裏面也就是更新下答案 就不註釋了

反正連我都懂的你們不可能看不懂是不是 所以下放這段的代碼

void getcir(int x,int y) {
	int siz = dep[y] - dep[x] + 1,t = siz;
	for (int a = y ; a != x ; a = fa[a]) bot[t--] = dp[a]; bot[t] = dp[x];
	for (int a = 1 ; a <= siz ; ++ a) bot[a + siz] = bot[a];
	int l = 0,r = 0; que[0] = 1;
	for (int a = 2 ; a <= siz << 1 ; ++ a) {
		while (l <= r && a - que[l] > siz >> 1) ++ l;
		ans = max(ans,bot[a] + a + bot[que[l]] - que[l]);
		while (l <= r && bot[que[r]] - que[r] < bot[a] - a) -- r;
		que[++r] = a;
	}
	for (int a = 2 ; a <= siz ; ++ a) dp[x] = max(dp[x],bot[a] + min(a - 1,siz - a + 1));
}

好了 下面就放完整代碼了 至於題目輸入什麼的 也不註釋了 這個太無腦了 =-=

這道 題目 (網址不同) 的代碼再放一下 =w=

#include <cstdio>
#define N 50010
inline int r() {
	char q = getchar(); int x = 0;
	while (q < '0' || q > '9') q = getchar();
	while ('0' <= q && q <= '9') x = x * 10 + q - 48,q = getchar();
	return x;
}
struct edge{int to,ne;}e[200010];
int first[N],dfn[N],low[N],dep[N],bot[N << 1],que[N],fa[N],dp[N],tot,ans;
inline int max(int x,int y) {return x > y ? x : y;}
inline int min(int x,int y) {return x < y ? x : y;}
void add(int x,int y) {
	e[++tot].ne = first[x],e[tot].to = y,first[x] = tot;
	e[++tot].ne = first[y],e[tot].to = x,first[y] = tot;
}
void getcir(int x,int y) {
	int siz = dep[y] - dep[x] + 1,t = siz;
	for (int a = y ; a != x ; a = fa[a]) bot[t--] = dp[a]; bot[t] = dp[x];
	for (int a = 1 ; a <= siz ; ++ a) bot[a + siz] = bot[a];
	int l = 0,r = 0; que[0] = 1; //話說這裏r和我的快讀重名了不過並沒有什麼事(好像慢了點)
	for (int a = 2 ; a <= siz << 1 ; ++ a) {
		while (l <= r && a - que[l] > siz >> 1) ++ l;
		ans = max(ans,bot[a] + a + bot[que[l]] - que[l]);
		while (l <= r && bot[que[r]] - que[r] < bot[a] - a) -- r;
		que[++r] = a;
	}
	for (int a = 2 ; a <= siz ; ++ a) dp[x] = max(dp[x],bot[a] + min(a - 1,siz - a + 1));
}
void tarjan(int p) {
	dfn[p] = low[p] = ++tot;
	for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
	if (b != fa[p]) {
		!dfn[b] ? fa[b] = p,dep[b] = dep[p] + 1,tarjan(b),low[p] = min(low[p],low[b])
				: low[p] = min(low[p],dfn[b]);
		if (low[b] > dfn[p]) ans = max(ans,dp[p] + dp[b] + 1),dp[p] = max(dp[p],dp[b] + 1);
	}
	for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
		if (fa[b] != p && dfn[p] < dfn[b]) getcir(p,b);
}
int main() {
	int n = r(),m = r();
	for (int b,x,k ; m > 0 ; -- m) {
		k = r(),x = r(); while (--k)
		b = r(),add(x,b),x = b;
	} tot = 0,tarjan(1),printf("%d\n",ans);
	return 0;
}

 

 

 

第二階段(圓方樹)

好了好了圓方樹搞定了 打程序全程靠概念 yy 加上各種判斷 感覺自己打的很毒瘤就是了 不過也就 4KBytes 不到

然後題目鏈接放這裏了 又是板子題

感謝 @Harry_bh 提供的 hack 數據 讓我受益匪淺emmm

話說打圓方樹我用的是樹剖嘛 像我這種會樹剖不會倍增 會線段樹不會樹狀數組的 實在是很少見了

而且我的代碼太奇怪了感覺肯定很有問題 可能是亂講一通

 

構造

搬一下 ImmortalCO 的課件裏面的幾句話

考慮爲邊設定邊權,先隨便取一個圓點當根,所有圓圓邊的邊權和 原圖中一致

對於每一條圓方邊: 如果它是方點的父邊,則定義它的邊權爲 0,否則定義其邊權爲 「這個圓點到方點的父親的最短路的長度」

現在,如果兩點的 LCA 是圓點,則兩點的最短路就是兩點的圓方樹上帶權距離(所有環都在已經決定了走較短一側)

否則,我們還需要考慮 LCA 這個環走哪一側,用樹鏈剖分或倍增 求出詢問的兩個點分別是在這個方點的哪兩個子樹中(即求出是環上的哪兩個點),然後環上取較短的一側

 

這裏我參(zhao)照(ban)了之前那道 仙人掌圖II 的遍歷方法 然後在找到環的時候 將其 環權值 賦值到 即將連接的方點上

我們沒必要每個點轉一次環環 沿環遍歷的同時 我們是從一邊到達該點的 記錄下來 循環完一圈後整個環的權值也記錄下來了 然後再一個點一個點將 之前那段路的權值 和 環減去那段路的值 取 min 即可

下放一下這段代碼

void getcir(int x,int y) {
	int len = tep[y] - tep[x] + 1,t = len; //len記錄環上點數 t是指針
	ll lon = 0; //lon記錄當前走的邊的權值和
	for (int p = y ; p != x ; p = ta[p]) bot[t--] = p; bot[t] = x; //通過父親數組取點
	for (int h = 1 ; h < len ; )
	for (int a = est[bot[h]],b = e[a].to ; ; a = e[a].ne,b = e[a].to)
		if (b == bot[h + 1]) {lon = lon + e[a].v,dis[++h] = lon; break;}
	for (int a = est[y],b = e[a].to ; ; a = e[a].ne,b = e[a].to)
		if (b == x) {lon = lon + e[a].v; break;} //搜環權值
	cir[++m] = lon; //記錄環權值
	for (int a = 1 ; a <= len ; ++ a) addf(bot[a],m,min(lon - dis[a],dis[a]));
} //(上面)圓方加邊
void tarjan(int p) { //用tarjan找環
	dfn[p] = low[p] = ++tnt;
	for (int a = est[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
	if (b != ta[p]) {
		!dfn[b] ? tep[b] = tep[p] + 1,ta[b] = p,tarjan(b),low[p] = min(low[p],low[b])
				: low[p] = min(low[p],dfn[b]);
		if (low[b] > dfn[p]) addf(p,b,e[a].v);
	}
	for (int a = est[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
		if (ta[b] != p && dfn[p] < dfn[b]) getcir(p,b);
}

那麼構造說完了 我們來講

 

建樹

@142857cs 提供了樹上前綴和的思路 感覺很好但我太頑固了實在是不想打 於是樹剖加上線段樹存權值

就是樹剖的兩個 dfs 啦 不過注意在 dfs1裏要加上邊權化點權 如果不會樹剖邊化點的可以去看看這個板子題

直接放代碼了 這是兩個深搜

void dfs1(int p) {
	dep[p] = dep[fa[p]] + 1,++siz[p];
	for (int a = fst[p],b = f[a].to ; a ; a = f[a].ne,b = f[a].to)
		if (b != fa[p]) {
		v[b] = f[a].v,fa[b] = p,dfs1(b),siz[p] += siz[b];
		if (siz[son[p]] < siz[b]) son[p] = b;
	}
}
void dfs2(int p,int an) { //an就是ancestor了,由於一些重名的原因..
	top[p] = an;
	id[p] = ++tot;
	oid[tot] = p;
	if (!son[p]) return;
	dfs2(son[p],an);
	for (int a = fst[p],b = f[a].to ; a ; a = f[a].ne,b = f[a].to)
		if (b != fa[p] && b != son[p]) dfs2(b,b);
}

這是一個建樹

void build(int l,int r,int len) {
	if (l == r) {tr[len] = v[oid[l]]; return;}
	int mid = (l + r) >> 1;
	build(l,mid,len << 1);
	build(mid + 1,r,len << 1 | 1);
	tr[len] = tr[len << 1 | 1] + tr[len << 1];
}

 

查詢

查詢着實是毒瘤

考慮方點圓點?不止!

這裏再次感謝 @Harry_bh 讓誤入歧途的我改過自新步入正軌

因爲方點下面跳的兩個點可能是兩輕邊呢=-= 那麼下面來講講

lca 爲圓點

直接搜索搜完了看看頂上那個點序號是不是大於 n 就好了

lca 爲方點

首先我們要減去方點連的兩圓點的權值

因爲點權記錄的是這個圓點到方點的父親的最短路的長度 我們最後又不一定要跑過去

 

然後下面來說說邊的問題

如果兩個都是輕邊 那麼可以通過前驅來記錄

如果其中一個是重邊 那麼我們就要把那個什麼的點的前驅改成 lca 的 重兒子 不然都不知道掉到哪裏去了

那放一下代碼 首先是求 lca 的

ll out(int x,int y) {
	int fx = x,fy = y;//前驅 這個不賦值都可以
	ll ans = 0;
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) swap(x,y),swap(fx,fy);
		ans += get(1,m,1,id[top[x]],id[x]);
		fx = top[x],x = fa[top[x]];
	} //跳樹剖
	if (x != y) {
		if (dep[x] > dep[y]) swap(x,y),swap(fx,fy);
		ans += get(1,m,1,id[x] + 1,id[y]);
	} //記錄終焉路徑
	if (x <= n) return ans; //lca爲圓點趕快退掉
	if (fy[fa] != fx[fa]) fy = son[x]; //把重鏈上的點提上來
	ans = ans - v[fx] - v[fy]; //減去多餘路徑
	if (tep[fx] > tep[fy]) swap(fx,fy); //這個因爲父親數組的原因要按dep排
	return ans + geft(fx,fy,cir[x]);
}

然後是 get 和 geft 這兩個東西

get 就是線段樹找連續一段的權值 這個樹剖模板裏有的 不多加闡述

geft 其實就是找兩點的最短路徑啦 某hkr 說這個也可以搞前綴記錄 不過我太懶了 每個詢問又跑了一遍環

所以應該是會被 hack 的 因爲假如查詢的環大 我這個要跑大半圈......

註釋放代碼裏面吧

ll get(int l,int r,int len,int i,int j) {
	if (i <= l && r <= j) return tr[len];
	int mid = (l + r) >> 1; ll ans = 0;
	if (i <= mid) ans += get(l,mid,len << 1,i,j);
	if (mid < j) ans += get(mid + 1,r,len << 1 | 1,i,j);
	return ans;
}
ll geft(int x,int y,int cirdis) { //cirdis是環的總權值 之前tarjan的時候記錄了的
	ll ans = 0;
	for (int p = y ; p != x ; p = ta[p]) //通過父親找到兩點距離
	for (int a = est[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
	if (b == ta[p]) {ans = ans + e[a].v; break;}
	return min(cirdis - ans,ans); //取最小 因爲兩條路嘛
}

 

好了 接下來是總代碼 隨便加點註釋吧

#include <algorithm>
#include <cstring>
#include <cstdio>
#define N 20010
#define M 100010
#define ll long long
#define swap std::swap
inline int re() {
	int x = 0; char w = getchar();
	while (w < '0' || w > '9') w = getchar();
	while ('0' <= w && w <= '9') x = x * 10 + w - 48,w = getchar();
	return x;
}
struct edge{int ne,to; ll v;}e[M],f[M];
int est[N],fst[N],dfn[N],low[N],tep[N],bot[N],ta[N],fr[N]; //這些是找環用的
int siz[N],son[N],dep[N],top[N],oid[N],fa[N],id[N]; //這些是樹剖用的 名字比較像
int tr[N << 2],v[N];
int tot,tnt,n = re(),m = re(),q = re();
ll cir[N],dis[N];
template <typename T> T min(T x,T y) {return x < y ? x : y;}
void adde(int x,int y,int z) {
	e[++tot].ne = est[x],e[tot].to = y,e[tot].v = z,est[x] = tot;
	e[++tot].ne = est[y],e[tot].to = x,e[tot].v = z,est[y] = tot;
}
void addf(int x,int y,ll z) {
	f[++tot].ne = fst[x],f[tot].to = y,f[tot].v = z,fst[x] = tot;
	f[++tot].ne = fst[y],f[tot].to = x,f[tot].v = z,fst[y] = tot;
}
void getcir(int x,int y) {
	int len = tep[y] - tep[x] + 1,t = len;
	ll lon = 0;
	for (int p = y ; p != x ; p = ta[p]) bot[t--] = p; bot[t] = x;
	for (int h = 1 ; h < len ; )
	for (int a = est[bot[h]],b = e[a].to ; ; a = e[a].ne,b = e[a].to)
		if (b == bot[h + 1]) {lon = lon + e[a].v,dis[++h] = lon; break;}
	for (int a = est[y],b = e[a].to ; ; a = e[a].ne,b = e[a].to)
		if (b == x) {lon = lon + e[a].v; break;}
	cir[++m] = lon;
	for (int a = 1 ; a <= len ; ++ a) addf(bot[a],m,min(lon - dis[a],dis[a]));
} //找環加方點 順便記錄環長度
void tarjan(int p) {
	dfn[p] = low[p] = ++tnt;
	for (int a = est[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
	if (b != ta[p]) {
		!dfn[b] ? tep[b] = tep[p] + 1,ta[b] = p,tarjan(b),low[p] = min(low[p],low[b])
				: low[p] = min(low[p],dfn[b]);
		if (low[b] > dfn[p]) addf(p,b,e[a].v);
	}
	for (int a = est[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
		if (ta[b] != p && dfn[p] < dfn[b]) getcir(p,b);
}
void dfs1(int p) {
	dep[p] = dep[fa[p]] + 1,++siz[p];
	for (int a = fst[p],b = f[a].to ; a ; a = f[a].ne,b = f[a].to)
		if (b != fa[p]) {
		v[b] = f[a].v,fa[b] = p,dfs1(b),siz[p] += siz[b];
		if (siz[son[p]] < siz[b]) son[p] = b;
	}
}
void dfs2(int p,int an) {
	top[p] = an;
	id[p] = ++tot;
	oid[tot] = p;
	if (!son[p]) return;
	dfs2(son[p],an);
	for (int a = fst[p],b = f[a].to ; a ; a = f[a].ne,b = f[a].to)
		if (b != fa[p] && b != son[p]) dfs2(b,b);
}
void build(int l,int r,int len) {
	if (l == r) {tr[len] = v[oid[l]]; return;}
	int mid = (l + r) >> 1;
	build(l,mid,len << 1);
	build(mid + 1,r,len << 1 | 1);
	tr[len] = tr[len << 1 | 1] + tr[len << 1];
}
ll get(int l,int r,int len,int i,int j) {
	if (i <= l && r <= j) return tr[len];
	int mid = (l + r) >> 1; ll ans = 0;
	if (i <= mid) ans += get(l,mid,len << 1,i,j);
	if (mid < j) ans += get(mid + 1,r,len << 1 | 1,i,j);
	return ans; //線段樹查詢鏈上權值和
}
ll geft(int x,int y,int cirdis) {
	ll ans = 0;
	for (int p = y ; p != x ; p = ta[p])
	for (int a = est[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
	if (b == ta[p]) {ans = ans + e[a].v; break;}
	return min(cirdis - ans,ans);
} //找環上距離通過之前的ta數組找較深點的父親跳上去 然後取最小值
ll out(int x,int y) {
	int fx = x,fy = y; //記前驅
	ll ans = 0;
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) swap(x,y),swap(fx,fy);
		ans += get(1,m,1,id[top[x]],id[x]);
		fx = top[x],x = fa[top[x]];
	} //跳樹剖
	if (x != y) {
		if (dep[x] > dep[y]) swap(x,y),swap(fx,fy);
		ans += get(1,m,1,id[x] + 1,id[y]);
	}
	if (x <= n) return ans; //如果lca是圓點就跳出去
	if (fy[fa] != fx[fa]) fy = son[x];
	ans = ans - v[fx] - v[fy]; //如果是方點就去掉方點下面兩點權值
	if (tep[fx] > tep[fy]) swap(fx,fy);
	return ans + geft(fx,fy,cir[x]); //跑環
}
int main() {
	for (int z,y,x ; m > 0 ; -- m) x = re(),y = re(),z = re(),adde(x,y,z);
	m = n,tot = 0,tarjan(1),dfs1(1),tot = 0,dfs2(1,1),build(1,m,1); //m到後來是存圓方樹上點數的
	for (int y1,x1 ; q > 0 ; -- q) x1 = re(),y1 = re(),printf("%lld\n",out(x1,y1));
	return 0;
}

 

 

 

第三階段(動態圓方樹)

這個我不會,實在找不到正常點的標

VFleaKing的我看不懂啊 =-= 到時候問一下InFleaKing吧(汗

完結撒花

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章