CodeForces 1268E Happy Cactus

发布时间 2023-07-28 08:41:10作者: zltzlt

洛谷传送门

AtCoder 传送门

考虑一些简单的情况,比如树。

\(f_u\) 为当前 \(u\) 能通过边权递增的路径到达的点数(包括它自己)。

为了让两个点对在边权递增路径的边权最小的那条边被统计,我们倒序枚举边。当枚举到 \((u, v)\) 时,我们有 \(f_u = f_v = f_u + f_v\)

这是因为 \(u\) 能通过 \((u, v)\) 这条边到达全部 \(v\) 能到达的点,并且 \((u, v)\) 的边权一定是最小的。\(v\) 同理。并且 \(u, v\) 此前不连通,所以能到达的点不会重复。

现在给定的是一棵仙人掌,我们可以照搬树的做法,但是出问题了。原因在于,我们枚举到边 \((u, v)\) 时,\(u, v\) 可能已经在环上有唯一一条简单路径了。

但是注意到,算重仅当环上存在点 \(w\) 使得 \(u \to w\) 的路径边权递增且 \(v \to w\) 的路径边权递增。那么此时 \(u \to v\) 的路径上边权先增后减。

设环上边权最大的那条边为 \((u', v')\)。此时我们会算重 \(u', v'\) 能到达的环外的点和点 \(u', v'\)

\(g_i\) 为枚举完边权为 \(i\) 的边 \((u, v)\)\(u, v\) 能到达的点数。注意我们只需要用到环上最大边的 \(g_i\),所以当 \(i\) 是环上最大边时,我们计算 \(g_i = f_u\)。当枚举到环上最小边 \((u, v)\) 时,设这个环的最大边权为 \(i\)。如果这个环的边权先增后减,我们有 \(f_u = f_v = f_u + f_v - g_i\)

我们剩下的任务是找环。因为给定的是仙人掌,所以环的边不会相交,我们直接建出 dfs 树,每一条返祖边就对应一个环。

时间复杂度 \(O(n + m)\)

code
// Problem: E. Happy Cactus
// Contest: Codeforces - Codeforces Round 609 (Div. 1)
// URL: https://codeforces.com/problemset/problem/1268/E
// Memory Limit: 256 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 500100;

int n, m, dep[maxn], stk[maxn], top, col[maxn], cnt, f[maxn], g[maxn];
int mne[maxn], mxe[maxn];
bool vis[maxn], mk[maxn];
pii E[maxn];
vector<pii> G[maxn];
vector<int> cir[maxn];

void dfs(int u, int fa) {
	vis[u] = 1;
	for (pii p : G[u]) {
		int v = p.fst, id = p.scd;
		if (v == fa) {
			continue;
		}
		if (vis[v]) {
			if (dep[v] < dep[u]) {
				++cnt;
				for (int i = 0; i < dep[u] - dep[v]; ++i) {
					int w = stk[top - i];
					col[w] = cnt;
					cir[cnt].pb(w);
				}
				col[id] = cnt;
				cir[cnt].pb(id);
				reverse(cir[cnt].begin(), cir[cnt].end());
			}
			continue;
		}
		dep[v] = dep[u] + 1;
		stk[++top] = id;
		dfs(v, u);
		--top;
	}
}

void solve() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; ++i) {
		scanf("%d%d", &E[i].fst, &E[i].scd);
		G[E[i].fst].pb(E[i].scd, i);
		G[E[i].scd].pb(E[i].fst, i);
	}
	dfs(1, -1);
	for (int i = 1; i <= cnt; ++i) {
		int mx = max_element(cir[i].begin(), cir[i].end()) - cir[i].begin();
		vector<int> vc;
		mxe[i] = cir[i][mx];
		for (int j = mx + 1; j < (int)cir[i].size(); ++j) {
			vc.pb(cir[i][j]);
		}
		for (int j = 0; j < mx; ++j) {
			vc.pb(cir[i][j]);
		}
		int mn = min_element(vc.begin(), vc.end()) - vc.begin();
		mne[i] = vc[mn];
		bool flag = 1;
		for (int j = 0; j < mn; ++j) {
			flag &= (vc[j] > vc[j + 1]);
		}
		for (int j = mn; j + 1 < (int)vc.size(); ++j) {
			flag &= (vc[j] < vc[j + 1]);
		}
		if (flag) {
			mk[i] = 1;
		}
	}
	for (int i = 1; i <= n; ++i) {
		f[i] = 1;
	}
	for (int i = m; i; --i) {
		int u = E[i].fst, v = E[i].scd;
		f[u] = f[v] = f[u] + f[v] - (mk[col[i]] && i == mne[col[i]] ? g[mxe[col[i]]] : 0);
		if (mk[col[i]] && i == mxe[col[i]]) {
			g[i] = f[u];
		}
	}
	for (int i = 1; i <= n; ++i) {
		printf("%d ", f[i] - 1);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}