【FJWC2019】子图(容斥)(计数)(三元环 / 四元环计数)

题意: n 1 e 5 n\le 1e5 个点, m 2 e 5 m\le 2e5 条边,求包含 k 4 k\le 4 条边的联通块个数


这里有图

k = 1 k=1 ,显然为 m m
k = 2 k=2 ,枚举中间点组合数算即可
k = 3 k=3 ,有三种情况,一种是三元环,一种是链,一种是菊花图
链:枚举中间边,算两边的贡献,但两边的出点重合会退化为三元环
每个三元环会算三次,菊花图枚举根算,然后三元计数把多的减掉


k = 4 k=4
发现共 5 种情况
在这里插入图片描述

第 3 个组合数算
第 4 个可以求出每个点所在的三元环个数然后枚举点算贡献
第 2 个可以枚举边 2 , 3 2,3 ,然后算 2 和 3 出去 1 个或 2 个的贡献,
当 1,4 或者 1,5 重合的时候回退化为 4,多算了两边需要减掉
第 1 个可以枚举 3,然后按顺序枚举 3 的出边,考虑当前边与之前的拼接
但退化有点烦
当 1 和 4 重合 或者是 2 和 5 重合会被多算,在图 4 中会在 3,4 被多算两次
当 1 5 重合会退化成 5,多算了 4 次
当 1 4 并且 2 5 重合会被算到三元环里面,多算 3 次

现在问题就是如何求四元环个数
依然按照三元环那样排序,我们定义 4 元环“权值”最大为根
需要保证一个四元环只会在最大的根算一次
枚举根以及出边,然后枚举出边的出点,对每个到根路径为 2 的点维护条数,加上之前的条数过后将条数加 1,然后再倒过来加一遍就可以把每一个点的贡献算上
代码奉上:

	static int ct[N], sm[N];
	for(int i=1; i<=n; i++){
		for(int j=0; j<v[i].size(); j++)
		if(cmp(i,v[i][j])){
			for(int e=0,t=v[i][j]; e<v[t].size(); e++)
			if(cmp(i,v[t][e])){
				int w = v[t][e];
				sm[i]+=ct[w]; sm[t]+=ct[w]; sm[w]+=ct[w]; ++ct[w];
			}
		}
		for(int j=0; j<v[i].size(); j++)
		if(cmp(i,v[i][j])){
			for(int e=0,t=v[i][j]; e<v[t].size(); e++)
			if(cmp(i,v[t][e])){
				int w = v[t][e]; sm[t] += --ct[w];
			}
		}
	} int ans = 0;
	for(int i = 1; i <= n; i++) Add(ans, sm[i]);
	return mul(ans, inv4);

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
cs int N = 1e5 + 5, M = 2e5 + 5;
cs int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b;}
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Add(int &a, int b){ a = add(a, b); }
cs int inv2 = (Mod+1)>>1, inv3 = (Mod+1)/3, inv6 = mul(inv2,inv3);
cs int inv4 = mul(inv2, inv2), inv24 = mul(inv4,inv6);
int n, m, k, x[M], y[M];
vector<int> v[N], A[N];
int C2(int x){ return mul(inv2, mul(x,x-1)); }
int C3(int x){ return mul(mul(inv6, x), mul(x-1, x-2)); }
int C4(int x){ return mul(mul(inv24, mul(x,x-1)), mul(x-2,x-3));}
namespace Subtask2{
	void Solve(){
		int ans = 0;
		for(int i=1; i<=n; i++) Add(ans, C2(v[i].size()));
		cout << ans;
	}
}
bool cmp(int x, int y){ return v[x].size() > v[y].size() || (v[x].size() == v[y].size() && x > y); }
int cir[N];
int Circle3(){
	int ans = 0;
	for(int i=1; i<=m; i++){
		if(cmp(x[i], y[i])) A[x[i]].push_back(y[i]);
		else A[y[i]].push_back(x[i]);
	}
	static int vis[N]; int TIME = 0;
	memset(vis, 0, sizeof(vis));
	memset(cir, 0, sizeof(cir));
	for(int i=1; i<=n; i++){
		++TIME;
		for(int j=0; j<A[i].size(); j++) vis[A[i][j]] = TIME;
		for(int j=0; j<A[i].size(); j++)
		for(int e=0,v=A[i][j]; e<A[v].size(); e++)
		if(vis[A[v][e]] == TIME) ++ans, ++cir[i], ++cir[v], ++cir[A[v][e]];
	} return ans;
}
namespace Subtask3{
	void Solve(){
		int ans = 0;
		for(int i=1; i<=n; i++) Add(ans, C3(v[i].size()));
		for(int i=1; i<=m; i++) Add(ans, mul(v[x[i]].size()-1, v[y[i]].size()-1));
		Add(ans, Mod-mul(2,Circle3())); cout << ans;
	}
}
int Circle4(){
	static int ct[N], sm[N];
	for(int i=1; i<=n; i++){
		for(int j=0; j<v[i].size(); j++)
		if(cmp(i,v[i][j])){
			for(int e=0,t=v[i][j]; e<v[t].size(); e++)
			if(cmp(i,v[t][e])){
				int w = v[t][e];
				sm[i]+=ct[w]; sm[t]+=ct[w]; sm[w]+=ct[w]; ++ct[w];
			}
		}
		for(int j=0; j<v[i].size(); j++)
		if(cmp(i,v[i][j])){
			for(int e=0,t=v[i][j]; e<v[t].size(); e++)
			if(cmp(i,v[t][e])){
				int w = v[t][e]; sm[t] += --ct[w];
			}
		}
	} int ans = 0;
	for(int i = 1; i <= n; i++) Add(ans, sm[i]);
	return mul(ans, inv4);
}
namespace Subtask4{
	int calc(){
		int ans = 0;
		for(int i=1; i<=n; i++) Add(ans, mul(cir[i], (int)v[i].size()-2));
		return ans;
	}
	void Solve(){
		int ans = 0;
		Add(ans, Mod-mul(3,Circle3()));
		for(int i=1; i<=n; i++) Add(ans, C4(v[i].size()));
		for(int i=1; i<=m; i++) 
		Add(ans, mul(C2(v[x[i]].size()-1),v[y[i]].size()-1)),
		Add(ans, mul(C2(v[y[i]].size()-1),v[x[i]].size()-1));
		Add(ans, Mod-mul(3,calc()));
		for(int i=1; i<=n; i++){
			int coe = 0;
			for(int e=0; e<v[i].size(); e++){
				int u = v[i][e];
				Add(ans,mul(coe, v[u].size()-1));
				Add(coe, v[u].size()-1);
			}
		} 
		Add(ans, Mod-mul(3,Circle4()));
		cout << ans; 
	}
}
int main(){
	n = read(), m = read(), k = read();
	for(int i = 1; i <= m; i++){
		x[i] = read(), y[i] = read();
		v[x[i]].push_back(y[i]);
		v[y[i]].push_back(x[i]);
	}
	if(k == 1){ cout << m; return 0; }
	if(k == 2){ Subtask2::Solve(); return 0;}
	if(k == 3){ Subtask3::Solve(); return 0;}
	if(k == 4){ Subtask4::Solve(); return 0;}
}
发布了610 篇原创文章 · 获赞 94 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/103497472