題目:https://pintia.cn/market/item/1795304158332379136
題意:有一個 \(n(1\leq n\leq 10^9)\) 個點的完全圖,其中有 \(m(m\leq 20)\) 條特殊的邊。每次操作會等機率地選擇這張圖的一棵生成樹,然後將樹上的邊染色。問期望多少次,能把所有 \(m\) 條特殊邊都染上色。
設 \(f_e\) 表示邊 \(e\) 第一次被染色的時間,特殊邊的集合為 \(S\),經典min-max容斥:
答案是
考慮 \(\min_{e\in T} f_e\) 的含義,即對這個邊集來說,有某條邊被染色(選中)的時間,這是經典的幾何分佈——假設選中 \(T\) 中某條邊的機率是 \(p_T\),則期望次數是 $$\sum_{i=1}^\infty i\times (1-p_T)^{i-1}\cdot p_T=\frac{1}{p_T}$$
因此只要計算機率 \(p_T\) 即可
要算選中至少一條邊的機率,再容斥一下:設 \(X_e\) 表示事件“邊 \(e\) 被選中”,則要求的是:
轉換成算某個邊集所有邊都被選中的機率,注意這裡可千萬別覺得好像“繞回去了”,一開始的問題是所有邊都被選中的次數期望,這裡被我們用兩次容斥轉換成了一個“簡單”得多的機率問題。
這是經典的圖連通計數,強制讓 \(U\) 中的邊連線,問有多少種加邊方案能得到一棵樹,假設有 \(k\) 個連通塊,其大小分別是 \(s_1,\dots,s_k\),則方案是 \(n^{k-2} \prod s_i\),因此機率就是 \(n^{n-k}\prod s_i\).
這樣我們可以在 \(O(2^m\cdot m\cdot \alpha(n))\) 的時間得到每個 \(P(\cap_{e\in U} X_e)\) 的答案,然後要得到 \(p_T\),是個經典高維字首和(參考:https://www.cnblogs.com/heyuhhh/p/11585358.html)
實現
注意點: 第二個容斥一定要記得 \(U\neq \emptyset\),我debug的大部分時間都花在這了…
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
const int MOD=1e9+7;
const int M=20;
int ksm(int a,int b){
int ret=1;a%=MOD;
for(;b;b>>=1,a=(ll)a*a%MOD)if(b&1)ret=(ll)ret*a%MOD;
return ret;
}
int n,m,u[M+1],v[M+1],f[(1<<M)+5],g[(1<<M)+5];
int fa[M*2+5],sz[M*2+5];
vector<int> b;
int find(int x){
if(x==fa[x])return x;
return fa[x]=find(fa[x]);
}
bool merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy)return false;
fa[fy]=fx;
sz[fx]+=sz[fy];
return true;
}
void dsu_init(){
rep(i,0,b.size())fa[i]=i,sz[i]=1;
}
void add(int &x,int y){
x+=y;
if(x>=MOD)x-=MOD;
}
int main(){
fastio;
cin>>n>>m;
rep(i,0,m-1){
cin>>u[i]>>v[i];
b.push_back(u[i]);
b.push_back(v[i]);
}
sort(b.begin(),b.end());
b.erase(unique(b.begin(),b.end()),b.end());
rep(i,0,m-1){
u[i]=lower_bound(b.begin(),b.end(),u[i])-b.begin();
v[i]=lower_bound(b.begin(),b.end(),v[i])-b.begin();
}
for(int S=0;S<(1<<m);S++){
dsu_init();
bool ok=true;
rep(i,0,m-1)if((S>>i)&1)ok&=merge(u[i],v[i]);
if(!ok)f[S]=0;
else{
f[S]=1;
int block=n-b.size();
rep(i,0,b.size()-1)if(find(i)==i){
block++;
f[S]=(ll)f[S]*sz[i]%MOD;
}
f[S]=(ll)f[S]*ksm(ksm(n,n-block),MOD-2)%MOD;
if((__builtin_popcount(S)&1)==0)f[S]=MOD-f[S];
}
g[S]=f[S];
}
rep(j,0,m-1)rep(S,0,(1<<m)-1)if(S>>j&1)add(f[S],f[S^(1<<j)]);
rep(S,0,(1<<m)-1)add(f[S],MOD-g[0]);
rep(S,0,(1<<m)-1)f[S]=ksm(f[S],MOD-2);
int ans=0;
rep(S,0,(1<<m)-1){
if(__builtin_popcount(S)&1)add(ans,f[S]);
else add(ans,MOD-f[S]);
}
cout<<ans;
return 0;
}