CodeForces 1268E Happy Cactus

洛谷传送门

AtCoder 传送门

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

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

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

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

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

但是注意到,算重仅当环上存在点 w 使得 uw 的路径边权递增且 vw 的路径边权递增。那么此时 uv 的路径上边权先增后减。

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

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

我们剩下的任务是找环。因为给定的是仙人掌,所以环的边不会相交,我们直接建出 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;
}
posted @   zltzlt  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2022-07-28 洛谷 P4899 [IOI2018] werewolf 狼人
2022-07-28 CodeForces 1628E Groceries in Meteor Town
2022-07-28 CodeForces 1416D Graph and Queries
2022-07-28 Kruskal 重构树学习笔记
2022-07-28 CodeForces 1706E Qpwoeirut and Vertices
点击右上角即可分享
微信分享提示