題意: n ≤ 1 e 5 n\le 1e5 n≤1e5 個點, m ≤ 2 e 5 m\le 2e5 m≤2e5 條邊,求包含 k ≤ 4 k\le 4 k≤4 條邊的聯通塊個數
這裡有圖
k = 1 k=1 k=1,顯然為 m m m
k = 2 k=2 k=2,枚舉中間點組合數算即可
k = 3 k=3 k=3,有三種情況,一種是三元環,一種是鍊,一種是菊花圖
鍊:枚舉中間邊,算兩邊的貢獻,但兩邊的出點重合會退化為三元環
每個三元環會算三次,菊花圖枚舉根算,然後三元計數把多的減掉
k = 4 k=4 k=4
發現共 5 種情況
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyAjM3ADMzgTMxEjMxkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
第 3 個組合數算
第 4 個可以求出每個點所在的三元環個數然後枚舉點算貢獻
第 2 個可以枚舉邊 2 , 3 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;}
}