Loading

ABC 262 E 题解

原题传送门

感觉是一道不是很难想到但还是比较不错的题。

题目要求将k个点染成红色,那么我们不妨首先将所有点视为蓝色,然后一个一个地将红点加进去。
那么就从简单的入手,考虑最初只加入一个红点,那么显然满足要求的边的数量就是这个红色点的度数。

然后考虑再加进去一个红色点,此时满足要求的边的数量还是会加上它的度数,但是显然会出问题:如果这两个红色点之前有边相连,那么它们之间的那条边就是不合法的,我们不应该统计它。

于是此题的重头戏就来了:这条不合法的边我们把它算了两次(加第一个点和第二个点的时候都统计了它),但是,我们把这两次减掉,不影响合法边数量的奇偶性。
所以就直接不用管它了。
再继续往里面加红色点也是同样的道理,每条边只要不合法就说明它连接了两个红色点,那么我们会把它统计两次,但实际上不影响奇偶性,所以可以直接不管。

于是此题就变得很简单了:从图中选出k个点使得度数之和为偶数。

然后我们考虑把所有点按照度数的奇偶性分类统计数量,然后度数为偶数的点加进去对总数的奇偶性没有任何影响,所以可以直接在度数为奇数的点中选出偶数个,其余的选择度数为偶数的点,两种分别求组合数然后用乘法原理统计起来就能得到答案了。

//MrcFrst_LRY 
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define re register
#define int long long
const int N=200200,mod=998244353;
int n,ans,m,k,d[N],cnt[2],inv[N],invfac[N],fac[N];
inline int read(){
    re int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}
il int C(int n,int m){
	if(n<m)return 0;
	return fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}//组合数
il void get_inv(){
	fac[0]=invfac[0]=inv[1]=invfac[1]=fac[1]=1;
	for(re int i=2;i<=n;i++){
		inv[i]=inv[mod%i]*(mod-mod/i)%mod;
		fac[i]=fac[i-1]*i%mod;
		invfac[i]=invfac[i-1]*inv[i]%mod;
	}
}//为计算组合数预处理逆元
signed main(){
	n=read(),m=read(),k=read();
	for(re int i=1;i<=m;i++){
		int u=read(),v=read();
		d[u]++,d[v]++;//统计度数
	}
	get_inv();
	for(re int i=1;i<=n;i++)cnt[d[i]&1]++;//按度数的奇偶性将点分类统计数量
	for(re int i=0;i<=k;i+=2)
		(ans+=(C(cnt[1],i)*C(cnt[0],k-i))%mod)%=mod;
	cout<<ans;
    return 0;
}
posted @ 2023-08-10 11:40  MrcFrst  阅读(14)  评论(0编辑  收藏  举报