洛谷 P1989 无向图三元环计数 做题记录

前置芝士:无

思路

神秘根号分治。
首先先把每个点的度数 \(deg\) 统计出来,其次给图进行定向,对于一对点 \(\{u,v\}\) 定向规则如下:

  • 如果 \(deg_u<deg_v\),那么有一条从 \(u\) 连向 \(v\) 的边。
  • 如果 \(deg_u=deg_v\)\(u<v\),那么也有一条从 \(u\) 连向 \(v\) 的边。

显然的,这张图会是一个 DAG。

然后我们枚举每一个点 \(i\),给 \(i\) 可达的所有点 \(u\) 打上时间戳 \(tag_u=i\)。然后我们枚举与 \(i\) 相连的所有点 \(u\),在枚举与 \(u\) 相连的所有点 \(v\),如果 \(tag_v=i\),那么这就是一个合法的三元环。

正确性证明

一个可达的无向三元环一定可以在图中表现为度数从小到大连边的一个环,故不会少计/多计任何的环数。

时间复杂度证明

时间复杂度为 \(O(m\sqrt{m})\),理由如下:
首先,我们知道时间复杂度为 \(\sum_{i=1}^{m} \deg'_v\),其中 \(\deg'_v\) 表示原图中边 \(\{u,v\}\)\(v\) 的出度。因为我们在枚举时相当于枚举了每一条边,再枚举了 \(v\) 的所有可达点。
其次证明 \(\deg'_v\)\(O(\sqrt{m})\) 的。

  • \(\deg'_v\le\sqrt{m}\) 时,显然新图的边数一定小于原图,所以为 \(O(\sqrt{m})\)
  • \(\deg'_v>\sqrt{m}\) 时,因为两点连边只会从度数少的点连向度数多的点,而度数大于 \(\sqrt{m}\) 的点只会有 \(O(\sqrt{m})\) 个,所以在新图里面一定是 \(O(\sqrt{m})\) 个。
    综上,时间复杂度为 \(O(m\sqrt{m})\)
点击查看代码
#include<bits/stdc++.h>

#define pii pair<int,int> 
#define pll pair<long long,long long> 
#define ll long long
#define i128 __int128

#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
#define in4(a,b,c,d) a=read(), b=read(), c=read(), d=read()
#define fst first 
#define scd second 
#define dbg puts("IAKIOI")

using namespace std;

int read() {
	int x=0,f=1; char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1); 
	for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }

const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }

#define maxn 200050

int n,m;
vector<int> G[maxn];
int deg[maxn],a[maxn],b[maxn];
int tag[maxn];

void work() {
	in2(n,m);
	For(i,1,m) {
		in2(a[i],b[i]);
		deg[a[i]]++,deg[b[i]]++;
	}
	For(i,1,m) {
		int u=a[i],v=b[i];
		if(deg[u]>deg[v]) swap(u,v);
		else if(deg[u]==deg[v]&&u>v) swap(u,v);
		G[u].push_back(v);
	}
	int ans=0;
	For(i,1,n) {
		for(auto u:G[i]) tag[u]=i;
		for(auto u:G[i]) for(auto v:G[u])
			if(tag[v]==i) ans++;
	}
	cout<<ans<<'\n';
}

signed main() {
//	freopen("data.in","r",stdin);
//	freopen("myans.out","w",stdout);
//	ios::sync_with_stdio(false); 
//	cin.tie(0); cout.tie(0);
	double stt=clock();
	int _=1;
//	_=read();
//	cin>>_;
	For(i,1,_) {
		work();
	}
	cerr<<"\nTotal Time is:"<<(clock()-stt)*1.0/1000<<" second(s)."<<'\n';
	return 0;
}
posted @ 2025-04-22 20:54  coding_goat_qwq  阅读(29)  评论(0)    收藏  举报