首页 > 其他分享 >CodeForces 1268E Happy Cactus

CodeForces 1268E Happy Cactus

时间:2023-07-28 09:01:24浏览次数:37  
标签:cnt vc int 边权 CodeForces cir maxn Cactus Happy

洛谷传送门

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;
}

标签:cnt,vc,int,边权,CodeForces,cir,maxn,Cactus,Happy
From: https://www.cnblogs.com/zltzlt-blog/p/17586672.html

相关文章

  • Codeforces Round 888 (Div. 3)记录
    A.EscalatorConversations#include<cstdio>#include<algorithm>#include<cmath>#include<vector>#include<string.h>#include<set>#include<string>#include<map>#include<iostream>#include<queue>......
  • Codeforces Round 618 (Div. 2)
    CodeforcesRound618(Div.2)https://codeforces.com/contest/1300A.Non-zero要求和,积都不为0,则先把全部0操作一次,然后再check和是否为0,是的话再对任意数操作一次即可。#include<bits/stdc++.h>usingnamespacestd;constintN=105;intn,x,s,ans;voidsolve......
  • Codeforces Round 888 (Div. 3) A-F
    A.EscalatorConversations题意:有一个扶梯,有n个人要站扶梯,这个扶梯有m个位置,第i个位置的高度为i*k,Vlad高H,第i个人高h[i],当且仅当两个人所处的位置高度加上自身身高刚好相同时才能谈话,问能和Vlad谈话的有多少人。Solution直接计算即可voidsolve(){ intn,m,k,H;cin>>n>>m>>......
  • Codeforces Round 888 (Div. 3) - D
    目录D.PrefixPermutationSumsCodeforcesRound888(Div.3)赛后摘记D.PrefixPermutationSums题意判断给定的长为n-1数组,是否为某个1~n的序列的前缀和数组漏了一个数形成的数组思路就是判断能否变回去,毫无感情的判断机器法一:统计给定前缀和数组的差分数组得......
  • Codeforces Round 888 (Div. 3)
    A.EscalatorConversations#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongvoidsolve(){intn,m,k,H;cin>>n>>m>>k>>H;vector<int>h(n);intres=0;for(inth,......
  • Codeforces Round 888 (Div. 3)F(异或小技巧)
    题意:给你一个数组长度为n的a数组,要求a数组的值为非负整数,再给你一个k,a的值全小于2的k次方,找到一个小于a的k次方的值x,再从a中找到两个值,让他们(ai⊕x)&(aj⊕x)最小结论:n个数的最小异或对的答案就是排序后最小的相邻异或和思路:(ai⊕x)&(aj⊕x)的最高位为1,可以把它先转换成二进制......
  • 【题解】Educational Codeforces Round 150(CF1841)
    赛时过了A-E,然后就开摆了,为什么感觉C那么无厘头[发怒][发怒]排名:25thA.GamewithBoard题目描述:Alice和Bob玩游戏,他们有一块黑板。最初,有\(n\)个整数\(1\)。Alice和Bob轮流操作,Alice先手。轮到时,玩家必须在棋盘上选择几个(至少两个)相等的整数,擦除它们,然后写一个......
  • Codeforces 1852A Ntarsis' Set 题解
    题目传送门:Codeforces1852ANtarsis'Set题意给定一个集合,里面初始有\(1,2,3...10^{1000}\),告诉你每天会拿掉其中的第\(a_1,a_2,a_3...a_n\)个,询问这样的\(k\)天之后剩下的最小的数是多少。分析思考如果\(x\)在这天没有被删掉,那么哪些被删掉的位置会对它产生什么......
  • Codeforces Round 886 (Div. 4) D - H
    D.BalancedRoundProblem-D-Codeforces双指针,贪心题意:​ 给出\(n\)个数,我们可以删去其中若干个数,使得删完后的数重新排列任意相邻的数之差绝对值不超过\(k\),输出最小删去数的个数思路:​ 很明显可以先将给出的数组进行排序。对于排序后的数组,我们可以将其分为若干个相邻......
  • CodeForces 725F Family Photos
    洛谷传送门CF传送门不错的贪心题。我们考虑一对照片只有一张的情况。那么先后手会按照\(a+b\)降序取。因为若\(a_i+b_i\gea_j+b_j\),变形得\(a_i-b_j\gea_j-b_i\)且\(b_i-a_j\geb_j-a_i\),所以对于双方先取\(i\)一定是不劣的。回到原题,如果\(a_{i......