首页 > 其他分享 >CF 191 D

CF 191 D

时间:2024-05-09 21:57:30浏览次数:7  
标签:int ed 191 CF low leftrightarrow using deg

一条边最多在一个简单环当中,我们发现有几种情况:

image

这个显然直接选一个环覆盖再加上一条 \(1\leftrightarrow 4\) 的边。如果 \(4\) 没有,那么也是直接选一个环覆盖。

image

如果是有两个及以上的”突出“的边,那么我们显然不用选环,而是把换拆成一些链,这样更优。这个图中就是拆成 \(4\leftrightarrow 1\leftrightarrow 3\leftrightarrow 2\) 和 \(1\leftrightarrow 2\leftrightarrow 5\)。

那么我们发现,所有可以直接覆盖的环都是”突出“的边小于等于 \(1\) 个的。剩下的选的都是链。

求链的个数,其实就是链的端点个数除以 \(2\)。显然当 \(deg_u\) 是偶数的时候,就相当于有若干条链进入它,再从它出去,一定不是一个链的端点;反之如果是奇数,一定会是。

容易发现我们计算 \(deg_u\) 是奇数的个数的时候可以不考虑环(因为没有”突出“的边的环的所有 \(deg\) 都为 \(2\))。

求环的话,用 tarjan 边双可以做到 \(\mathcal{O}(n)\)。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 5e5+5;

struct edge {
	int nxt,to;
} ed[N<<1];

int lnk[N],tot=1,deg[N],vis[N];
int n,m,tim,dfn[N],low[N],drg[N],brd[N<<1],vid[N],cc;
vector<int> cir[N];

void ade(int u,int v){
	ed[++tot]={lnk[u],v};
	lnk[u]=tot,deg[u]++;
}

void tarjan(int u,int fa){
	dfn[u]=low[u]=++tim;
	for (int i=lnk[u],v; v=ed[i].to,i; i=ed[i].nxt){
		if (!dfn[v]){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if (low[v]>dfn[u]){
				brd[i]=brd[i^1]=1;
			}
		}
		else if (v^fa){
			low[u]=min(low[u],dfn[v]);
		}
	}
}

void dfs(int u){
	vis[u]=1;
	cir[cc].push_back(u);
	for (int i=lnk[u],v; v=ed[i].to,i; i=ed[i].nxt){
		if (!brd[i] && !vis[v]){
			dfs(v);
		}
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n>>m;
	for (int i=1; i<=m; i++){
		int u,v;
		cin>>u>>v;
		ade(u,v);
		ade(v,u);
	}
	if (!m){
		cout<<0<<" "<<0<<"\n";
		return 0;
	}
	for (int i=1; i<=n; i++){
		tarjan(i,0);
	}
	for (int i=1; i<=n; i++){
		if (!vis[i]){
			cc++;
			dfs(i);
		}
	}
	if (cc==1){
		cout<<1<<" "<<m<<"\n";
		return 0;
	}
	int ans=0;
	for (int i=1; i<=n; i++){
		if (deg[i]&1){
			ans++;
		}
	}
	ans/=2;
	for (int i=1; i<=cc; i++){
		if (cir[i].size()!=1){
			int cnt=0;
			for (auto u : cir[i]){
				if (deg[u]>2){
					cnt++;
				}
			}
			if (cnt<=1){
				ans++;
			}
		}
	}
	cout<<ans<<" "<<m<<"\n";
	return 0;
}

但是我翻了一下最短代码,好像有很短的。现在来研究怎么把代码写简单。先贴代码。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 1e5+5;

int n,m;
vector<int> g[N];

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n>>m;
	for (int i=1; i<=m; i++){
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	int ans=0;
	for (int i=1; i<=n; i++){
		if (g[i].size()&1){
			ans++;
		}
	}
	for (int i=1; i<=n; i++){
		if (g[i].size()==2){
			int x=g[i][0],y=g[i][1];
			if (x==y){
				g[x].clear();
				ans+=2;
			}
			else{
				g[x][g[x][0]!=i]=y;
				g[y][g[y][0]!=i]=x;
			}
		}
	}
	cout<<ans/2<<" "<<m<<"\n";
	return 0;
}

首先,因为找 \(deg_u\) 奇数个数不会有任何影响,所以我们先求这一个。下面的操作相当于求环。

我们知道,满足条件的环一定是最多有一个”突出“的边的,因此优两种情况:\(0\) 个和 \(1\) 个。分别考虑:

  • 如果是 \(0\) 个,下面的操作显然可行。其实这个连通块就是一个环,我们每一次选一个点把点两边的连起来再把这个点删掉。
  • 如果是 \(1\) 个,那么我们在没有”突出“的边的部分可以像上面进行一样的操作。其实可以不管那个有”突出“的边的部分,最后还是会压缩成 \(1 \leftrightarrow 2\),\(1\leftrightarrow 2\),\(2\leftrightarrow 3\) 的形式。遇到那个有”突出“的边的部分循环会跳过。

因此,这就有了一个及其好写的 \(\mathcal{O}(n)\) 做法。

标签:int,ed,191,CF,low,leftrightarrow,using,deg
From: https://www.cnblogs.com/SFlyer/p/18183137

相关文章

  • CF516E 做题记录
    link纪念一下独立切的*3100的数论+贪心题,思考时的思路一波三折,像极了考试中的我。个人感觉难度至少*3300。考虑先求出\(d=\gcd(n,m)\),那么编号根据模\(d\)结果分成了\(0...d-1\)共\(d\)个部分,每个部分里的人不能和外面的人玩。因此当\(d>b+g\)时一定无解,......
  • 易基因:cfDNA甲基化组多模式分析早期检测食管鳞状细胞癌和癌前病变|Nature子刊
    大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。食管癌(EC)是最常见的胃肠道恶性肿瘤之一,食管鳞状细胞癌(ESCC)是EC的主要组织学亚型,约占新EC病例的88%,大多数发生在东亚和中亚。与晚期ESCC的预后极差相比,早期ESCC(如黏膜内ESCC)和前体病变(如上皮内瘤变,IEN)可以通过内镜下整......
  • CF55D Beautiful numbers
    题目链接:https://www.luogu.com.cn/problem/CF55D数位dp解法:所有非零位都能整除这个数,那么就是说这些非零位的公倍数能够整除这个数。那么按照通常情况我们定义dp数组的时候应该定义成dp[pos][num][gbs],表示当前枚举到了第几位、上次枚举到的数、之前所有数位的最小公倍数。那......
  • [题解]CF1907G Lights
    CF1907GLights我们可以把灯抽象成节点,而开关抽象成无向边(重边算作\(1\)条)。显然每个连通块要么是一棵树,要么是一棵基环树。对于基环树,我们把它看做若干棵树处理,最后我们再考虑如何处理环。如下图,这是一棵树,黄色的点表示亮灯。我们选定任意一条边,可以改变子节点和父节点的状......
  • cf433c-ti-jie
    CF433C思路出于习惯,调换$n$和$m$,$n$为数组长度,$m$为值域。考虑枚举被替换的$a_i$。枚举值域$1$到$m$的权值$x$。每个权值为$x$的点$a_i$的贡献是$\mida_i-a_{i-1}\mid+\mida_i-a_{i+1}\mid$。由于$a_i$被更改,贡献会随之变化,与之有关的是所有$a_i$的左......
  • cf396c-ti-jie
    CF396C思路对于一个点维护$b_i=a_i-a_{fa_i}$。对于操作一,等价于$b_u$加$x$,$u$的子树不含$u$的每个点和父亲的差都减$k$。对于操作二,等价于从根到$u$路径上的$b_x$的和。同P3178,子树加,路径查,树剖加线段树。codeintn,q;inthead[maxn],tot;structnd{ intnxt......
  • CF1967C题解
    CF1906D这里更容易进入且有翻译题意对于数组\(a\),有函数\(f(a)\),设数组\(s=f(a)\),则对于每一个\(s_i\),都有:\[s_i=\left(\sum^{i}_{j=(i\operatorname{\&}(i-1))+1}{a_j}\right)\]其中\(\&\)表示按位与运算。对于一个正整数\(k\),函数\(f^k(a)\)定义如......
  • 2024CVPR_Low-light Image Enhancement via CLIP-Fourier Guided Wavelet Diffusion(C
    一、Motivation1、单模态监督问题:大多数方法往往只考虑从图像层面监督增强过程,而忽略了图像的详细重建和多模态语义对特征空间的指导作用。这种单模态监督导致不确定区域的次优重建和较差的局部结构,导致视觉结果不理想的出现。------》扩散模型缺乏有效性约束,容易出现多种生成效......
  • CF566E 做题记录
    link比较常规的一道构造题,练习自己的构造水平。首先对于一条边\((u,v)\),如果有边\((x,u),(v,y)\),我们可以对\(x,y\)的距离不超过\(2\)的点集\(S_x,S_y\)进行求交\(S_x\capS_y\),结果恰好就是\(\{u,v\}\)。我们枚举两条信息,对两个集合求交,如果结果为两个点,那么这两个......
  • CF1767C
    我们先讨论,对序列\(A=a_{ij}a_{i+1j}...a_{j-1j}a_{jj}\)来说什么是合法情况:不难发现当\(i==j\)时,\(a_{ij}=0\)与\(a_{ij}=1\)情况相同,而\(a_{ij}=2\)的情况不存在,此时\(a_{ij}=2\)就是不合法的情况。再来想象下这样一种情况:\(a_{kj}=0(k<j)\)这里有两个问题:k......