专栏文章
三元环计数学习笔记 || CF985G Team Players
CF985G题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @miqtilin
- 此快照首次捕获于
- 2025/12/04 10:29 3 个月前
- 此快照最后确认于
- 2025/12/04 10:29 3 个月前
本文着重讲解三元环计数问题。
三元环计数
题意
给你 个点 条边的无向图,求三元环的个数。。
题解
先考虑暴力。
枚举三元环中的其中一个点 ,再枚举 邻域中的一个点 ,再枚举 邻域中的一个点 ,判断 是否在 的邻域中。
枚举 和 实际上就是枚举了图中的边,所以暴力的时间复杂度是 的。
我们考虑给无向边定向,把无向图变成 DAG。
我们按照度数把点排序,度数小的连向度数大的。为了使图为 DAG,当度数相同时,编号小的连向编号大的,不然可能连出环。
我们发现按度数连边,每个点的出度不会很大。
- 当点的度数 时,出度也 。
- 当点的度数 时,假设出度 ,那么就有至少 个点的度数 ,显然矛盾。
所以每个点的出度都 。
接下来我们只需要去统计形如 的三元环,枚举 的这一步就优化成了 。
总时间复杂度为 。
例题 CF985G Team Players
题意
给你 个点 条边的无向图。
如果一个点的三元组 两两无边,那么它的贡献为 。
求出所有三元组的贡献和。
题解
题解中点的编号从 开始。
直接做边太多了,考虑容斥。分四种情况考虑。
- 边数 。
- 直接暴力数三元组即可。
- 贡献为
- 时间复杂度 。
- 边数 。
- 枚举这条边 ,钦定 ,考虑第三个点 的位置。
- 时,贡献为
- 时,贡献为
- 时,贡献为
- 时间复杂度 。
- 边数 。
- 枚举两条边的交点 ,枚举 邻域中的点 ,考虑第三个点 的位置。钦定 。
- 设 为邻域中编号小于 的点的数量, 为 在邻域中的排名,,。
- 当 时,贡献为
- 当 时,贡献为
- 当 时,贡献为
- 时间复杂度 。
- 边数 。
- 其实就是三元环计数。时间复杂度 。
简单容斥可得 。
代码
CPP#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;
const int N=2e5+10;
int n,m,d[N],vis[N];
ll C0,C1,C2,C3,A,B,C;
int fst[N<<1],to[N<<1],nxt[N<<1],ecnt;
void adde(int u,int v){
ecnt++;
to[ecnt]=v;
nxt[ecnt]=fst[u];
fst[u]=ecnt;
}
struct edge{int u,v;}E[N];
vector<int>e[N];
int main(){
cin>>n>>m>>A>>B>>C;
for(int i=1;i<=n;i++){//0
C0+=(ll)(n-i)*(n-i-1)/2*A*(i-1);
C0+=(ll)(i-1)*(n-i)*B*(i-1);
C0+=(ll)(i-1)*(i-2)/2*C*(i-1);
}
for(int i=1;i<=n;i++)e[i].push_back(0);
for(int i=1,u,v;i<=m;i++){//1
cin>>u>>v;u++,v++;
d[u]++,d[v]++;
E[i]=(edge){u,v};
e[u].push_back(v);
e[v].push_back(u);
if(u>v)swap(u,v);
C1+=(u-1)*((u-1)*B+(v-1)*C)+(ll)(u-2)*(u-1)/2*A;
C1+=(v-u-1)*((u-1)*A+(v-1)*C)+(ll)(v+u-2)*(v-u-1)/2*B;
C1+=(n-v)*((u-1)*A+(v-1)*B)+(ll)(v+n-1)*(n-v)/2*C;
}
for(int i=1;i<=n;i++)sort(e[i].begin(),e[i].end());
for(int u=1;u<=n;u++){//2
ll rk=0,cntr=0,cnt=0;
for(int i=1;i<=d[u];i++){
if(e[u][i]<u)rk=i,cntr+=e[u][i]-1;
else break;
}
for(int i=1;i<=d[u];i++){
int v=e[u][i];
if(v<u)C2+=(i-1)*((u-1)*C+(v-1)*B)+cnt*A;
else{
C2+=(i-rk-1)*((u-1)*A+(v-1)*C)+(cnt-cntr)*B;
C2+=rk*((u-1)*B+(v-1)*C)+cntr*A;
}
cnt+=v-1;
}
}
for(int i=1;i<=m;i++){
if(d[E[i].u]<d[E[i].v]||(d[E[i].u]==d[E[i].v]&&E[i].u<E[i].v))adde(E[i].u,E[i].v);//注意这一行
else adde(E[i].v,E[i].u);
}
for(int u=1;u<=n;u++){//3
for(int i=fst[u];i;i=nxt[i])vis[to[i]]=1;
for(int i=fst[u];i;i=nxt[i]){
int v=to[i];
for(int j=fst[v];j;j=nxt[j]){
int w=to[j];
if(vis[w])C3+=(min({u,v,w})-1)*A+(u+v+w-min({u,v,w})-max({u,v,w})-1)*B+(max({u,v,w})-1)*C;
}
}
for(int i=fst[u];i;i=nxt[i])vis[to[i]]=0;
}
cout<<C0-C1+C2-C3;
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...