[Luogu P4630] [BZOJ 5463] [APIO2018] Duathlon 鐵人兩項

洛谷傳送門

BZOJ傳送門

題目描述

比特鎮的路網由 mm 條雙向道路連接的 nn 個交叉路口組成。

最近,比特鎮獲得了一場鐵人兩項錦標賽的主辦權。這場比賽共有兩段賽程:選手先完成一段長跑賽程,然後騎自行車完成第二段賽程。

比賽的路線要按照如下方法規劃:

  1. 先選擇三個兩兩互不相同的路口 s,cs, cff,分別作爲比賽的起點、切換點(運動員在長跑到達這個點後,騎自行車前往終點)、終點。
  2. 選擇一條從 ss 出發,經過 cc 最終到達 ff 的路徑。考慮到安全因素,選擇的路徑經過同一個點至多一次。

在規劃路徑之前,鎮長想請你幫忙計算,總共有多少種不同的選取 s,cs, cff的方案,使得在第 22步中至少能設計出一條滿足要求的路徑。

輸入輸出格式

輸入格式:

第一行包含兩個整數 nnmm ,分別表示交叉路口和雙向道路的數量。

接下來 mm行,每行兩個整數 vi,uiv_i, u_i 。表示存在一條雙向道路連接交叉路口 vi,uiv_i, u_i (1vi,uin,viui)(1 ≤ v_i, u_i ≤ n,v_i \neq u_i)

保證任意兩個交叉路口之間,至多被一條雙向道路直接連接。

輸出格式:

輸出一行,包括一個整數,表示能滿足要求的不同的選取 s,cs, cff 的方案數。

輸入輸出樣例

輸入樣例#1:

4 3
1 2
2 3
3 4

輸出樣例#1:

8

輸入樣例#2:

4 4
1 2
2 3
3 4
4 2

輸出樣例#2:

14

解題分析

顯然縮出點雙後, 確定兩點之間可以選的中間點就是路徑上所有點和點雙的點數之和。

考慮把單獨一條邊也視爲一個點雙, 那麼建出圓方樹, 把方點的權值設爲點雙的大小, 原點的權值設爲1-1, 路徑上可選中間點數恰好就是路徑和, 然後可以轉化爲計算每點的貢獻。

總複雜度O(N)O(N)

代碼如下:

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define MX 200500
#define gc getchar()
#define ll long long
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
int n, m, top, arr, dcnt, sum, cnt;
int head[MX], val[MX], siz[MX], dfn[MX], low[MX], sta[MX], h[MX];
ll ans;
struct Edge {int to, nex;} edge[MX << 2];
IN void add1(R int from, R int to)
{edge[++cnt] = {to, head[from]}, head[from] = cnt;}
IN void add2(R int from, R int to)
{edge[++cnt] = {to, h[from]}, h[from] = cnt;}
void tarjan(R int now)
{
	dfn[now] = low[now] = ++dcnt;
	sta[++top] = now; val[now] = -1; ++sum;
	for (R int i = head[now]; i; i = edge[i].nex)
	{
		if (!dfn[edge[i].to])
		{
			tarjan(edge[i].to);
			low[now] = min(low[now], low[edge[i].to]);
			if (low[edge[i].to] == dfn[now])
			{
				add2(now, ++arr); val[arr] = 1; int tp = 0;
				do
				{
					tp = sta[top--]; add2(arr, tp);
					++val[arr];
				} while (tp ^ edge[i].to);
			}
		}
		else low[now] = min(low[now], dfn[edge[i].to]);
	}
}
void solve(R int now)
{
	if (now <= n) siz[now] = 1;
	for (R int i = h[now]; i; i = edge[i].nex)
	{
		solve(edge[i].to);
		ans += 1ll * siz[now] * siz[edge[i].to] * val[now];
		siz[now] += siz[edge[i].to];
	}
	ans += 1ll * (sum - siz[now]) * siz[now] * val[now];
}
int main(void)
{
	int a, b; in(n), in(m); arr = n;
	for (R int i = 1; i <= m; ++i)
	{
		in(a), in(b);
		add1(a, b), add1(b, a);
	}
	for (R int i = 1; i <= n; ++i)
	if (!dfn[i]) sum = 0, tarjan(i), solve(i);
	printf("%lld\n", ans * 2);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章