题解 [CF468E] Permanent / 归田
考虑将 \(A_{i, p_i}=w\) 转化为一条连接 \(i, p_i\),权值为 \(w\) 的边
那么问题转化为二分图完美匹配
将 \(w_i\) 拆为两条边 \((w_i-1), 1\)
这样任意两个点之间都至少有一条权为 1 的边
这样的好处是解除了完美匹配的限制,任意一个匹配都可以产生贡献
那么答案是
\[\sum\limits_{s是一个匹配}(n-|s|)!\prod\limits_{i\in s}(w_i-1)
\]
然后算后面那个东西
现在有 \(O(2k)\) 个点
考虑对每个连通块分别处理
每个块内有 \(\leqslant k\) 条边所以有 \(\leqslant k+1\) 个点
然后又是二分图所以一定有一边有 \(\leqslant\frac{k}{2}\) 个点
然后就有了一个 \(O(k2^k)\) 的做法
然后考虑另一个处理方式
若连通块内有 \(m\) 条边,先找一棵生成树出来
\(O(2^{m-n+1})\) 枚举非树边的选择情况,然后在生成树上 DP 匹配
复杂度 \(O(n^22^{m-n+1})\)
将两个做法拼在一起,复杂度 \(O(k^22^{\frac{k}{3}})\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 2000010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, k;
ll fac[N];
#undef unix
const ll mod=1e9+7;
int x[N], y[N], w[N];
int unix[N], uniy[N], sta[N], xsiz, ysiz;
inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}
// namespace task1{
// int now;
// ll f[2][55], ans;
// map<int, int> mp;
// map<int, ll> val;
// void solve() {
// int cnt=0;
// for (int i=1; i<=k; ++i) ++mp[y[i]], val[y[i]]=(val[y[i]]+w[i])%mod;
// for (int t=0; t<=mp.size(); ++t) {
// // cout<<"t: "<<t<<endl;
// memset(f[now], 0, sizeof(f[now]));
// f[now][0]=1; int cnt=0;
// for (auto it:mp) {
// memset(f[now^1], 0, sizeof(f[now^1]));
// // cout<<"it: ("<<it.fir<<','<<it.sec<<")"<<endl;
// // cout<<"val: "<<val[it.fir]<<endl;
// for (int i=0; i<=t; ++i) {
// ll tem=1;
// bool flag=cnt==i;
// for (int j=n-t-(cnt-i)-flag; j>n-t-(cnt-i)-flag-it.sec; --j)
// tem=tem*j%mod;
// f[now^1][i]=(f[now^1][i]+f[now][i]*tem)%mod;
// tem=val[it.fir];
// for (int j=n-t-(cnt-i); j>n-t-(cnt-i)-it.sec+1; --j)
// tem=tem*j%mod;
// f[now^1][i+1]=(f[now^1][i+1]+f[now][i]*tem)%mod;
// }
// // cout<<"f: "; for (int k=0; k<=t; ++k) cout<<f[now^1][k]<<' '; cout<<endl;
// cnt+=it.sec; now^=1;
// }
// ll tem=f[now][t];
// for (int i=n-cnt; i; --i) tem=tem*i%mod;
// // cout<<"add: "<<tem<<endl;
// ans=(ans+tem)%mod;
// }
// printf("%lld\n", ans);
// }
// }
// namespace force{
// #undef unix
// ll f[2][1<<20], ans;
// vector<pair<int, int>> buc[N];
// int unix[N], uniy[N], sta[N], xsiz, ysiz, now;
// void solve() {
// for (int i=1; i<=k; ++i) {
// unix[++xsiz]=x[i];
// uniy[++ysiz]=y[i];
// }
// sort(unix+1, unix+xsiz+1);
// sort(uniy+1, uniy+ysiz+1);
// xsiz=unique(unix+1, unix+xsiz+1)-unix-1;
// ysiz=unique(uniy+1, uniy+ysiz+1)-uniy-1;
// for (int i=1; i<=k; ++i) {
// x[i]=lower_bound(unix+1, unix+xsiz+1, x[i])-unix;
// y[i]=lower_bound(uniy+1, uniy+ysiz+1, y[i])-uniy;
// buc[x[i]].pb({y[i]-1, w[i]}), sta[x[i]]|=1<<y[i]-1;
// }
// if (ysiz>20) {task1::solve(); exit(0);}
// int lim=1<<ysiz;
// f[now][0]=1;
// for (int i=1; i<=xsiz; ++i,now^=1) {
// memset(f[now^1], 0, sizeof(f[now^1]));
// for (int s=0; s<lim; ++s) {
// f[now^1][s]=(f[now^1][s]+f[now][s]*(n-ysiz-(i-__builtin_popcount(s)-1)))%mod;
// for (auto& it:buc[i]) if (!(s&(1<<it.fir)))
// f[now^1][s|(1<<it.fir)]=(f[now^1][s|(1<<it.fir)]+f[now][s]*it.sec)%mod;
// for (int j=0; j<ysiz; ++j) if (!(s&(1<<j)) && !(sta[i]&(1<<j)))
// md(f[now^1][s|(1<<j)], f[now][s]);
// }
// }
// for (int s=0; s<lim; ++s) ans=(ans+f[now][s])%mod;
// for (int i=n-xsiz; i; --i) ans=ans*i%mod;
// printf("%lld\n", (ans%mod+mod)%mod);
// }
// }
namespace task2{
bool intr[105][105], vis[N];
vector<pair<int, int>> to[N];
int id1[N], id2[N], rk[N], sub[N], tot, siz;
ll f[2][1<<22], dp[105][2][55], tem[2][55], g[N], h[N], t[N], ans;
vector<pair<vector<int>, vector<int>>> sta;
void dfs(int u, int col, pair<vector<int>, vector<int>>& tem) {
vis[u]=1;
(col?tem.fir:tem.sec).pb(u);
for (auto& v:to[u]) if (!vis[v.fir])
dfs(v.fir, col^1, tem);
}
void solve1(pair<vector<int>, vector<int>>& it) {
int lim=1<<it.fir.size(), now=0;
for (int s=0; s<lim; ++s) f[now][s]=0;
for (int i=0; i<it.fir.size(); ++i) rk[it.fir[i]]=i;
f[now][0]=1;
for (auto& u:it.sec) {
// cout<<"u: "<<u<<endl;
for (int s=0; s<lim; ++s) f[now^1][s]=f[now][s];
for (int s=0; s<lim; ++s) if (f[now][s]) {
for (auto& v:to[u]) if (!(s&(1<<rk[v.fir]))) {
// cout<<"v: "<<v.fir<<' '<<v.sec<<endl;
f[now^1][s|(1<<rk[v.fir])]=(f[now^1][s|(1<<rk[v.fir])]+f[now][s]*v.sec)%mod;
}
}
now^=1;
}
// cout<<"f: "; for (int s=0; s<lim; ++s) cout<<f[now][s]<<' '; cout<<endl;
// cout<<"g: "; for (int i=0; i<=siz; ++i) cout<<g[i]<<' '; cout<<endl;
for (int i=0; i<=it.fir.size(); ++i) h[i]=0;
for (int s=0; s<lim; ++s) md(h[__builtin_popcount(s)], f[now][s]);
// cout<<"h: "; for (int i=0; i<=it.fir.size(); ++i) cout<<h[i]<<' '; cout<<endl;
}
void dfs2(int u) {
vis[u]=1;
for (auto v:to[u]) if (!vis[v.fir]) {
intr[u][v.fir]=intr[v.fir][u]=1;
dfs2(v.fir);
// cout<<"link: "<<u<<' '<<v.fir<<' '<<v.sec<<endl;
}
}
void dfs3(int u, int fa) {
// cout<<"dfs3: "<<u<<' '<<fa<<endl;
sub[u]=1;
dp[u][0][0]=!vis[u]; dp[u][1][0]=vis[u];
for (auto& v:to[u]) if (v.fir!=fa && intr[u][v.fir]) {
dfs3(v.fir, u);
// cout<<"merge: "<<u<<' '<<v.fir<<endl;
// cout<<"dp0: "; for (int i=0; i<=sub[u]; ++i) cout<<dp[u][0][i]<<' '; cout<<endl;
// cout<<"dp1: "; for (int i=0; i<=sub[u]; ++i) cout<<dp[u][1][i]<<' '; cout<<endl;
for (int i=0; i<=sub[u]+sub[v.fir]; ++i) tem[0][i]=tem[1][i]=0;
for (int i=0; i<=sub[u]; ++i) {
for (int j=0; j<=sub[v.fir]; ++j) {
tem[0][i+j]=(tem[0][i+j]+dp[u][0][i]*(dp[v.fir][0][j]+dp[v.fir][1][j]))%mod;
tem[1][i+j]=(tem[1][i+j]+dp[u][1][i]*(dp[v.fir][0][j]+dp[v.fir][1][j]))%mod;
tem[1][i+j+1]=(tem[1][i+j+1]+dp[u][0][i]*dp[v.fir][0][j]%mod*v.sec)%mod;
}
}
sub[u]+=sub[v.fir];
for (int i=0; i<=sub[u]; ++i) dp[u][0][i]=tem[0][i], dp[u][1][i]=tem[1][i];
// cout<<"tem0: "; for (int i=0; i<=sub[u]; ++i) cout<<tem[0][i]<<' '; cout<<endl;
// cout<<"tem1: "; for (int i=0; i<=sub[u]; ++i) cout<<tem[1][i]<<' '; cout<<endl;
}
// cout<<"dp0: "; for (int i=0; i<=sub[u]; ++i) cout<<dp[u][0][i]<<' '; cout<<endl;
// cout<<"dp1: "; for (int i=0; i<=sub[u]; ++i) cout<<dp[u][1][i]<<' '; cout<<endl;
// cout<<"return "<<u<<endl;
}
void solve2(pair<vector<int>, vector<int>>& it, int m) {
int n=it.fir.size()+it.sec.size();
for (auto& t:it.fir) vis[t]=0;
for (auto& t:it.sec) vis[t]=0;
dfs2(it.fir[0]);
vector<pair<int, pair<int, int>>> sta;
for (auto& u:it.fir)
for (auto& v:to[u]) if (!intr[u][v.fir])
sta.pb({u, v});
// cout<<"sta: "; for (auto it:sta) cout<<"("<<it.fir<<','<<it.sec.fir<<','<<it.sec.sec<<") "; cout<<endl;
int lim=1<<sta.size();
// cout<<"lim: "<<lim<<endl;
for (int i=0; i<=it.fir.size(); ++i) h[i]=0;
for (int s=0; s<lim; ++s) {
for (auto& u:it.fir) {
vis[u]=0;
for (int i=0; i<=n; ++i) dp[u][0][i]=dp[u][1][i]=0;
}
for (auto& u:it.sec) {
vis[u]=0;
for (int i=0; i<=n; ++i) dp[u][0][i]=dp[u][1][i]=0;
}
ll val=1;
for (int i=0; i<sta.size(); ++i) if (s&(1<<i)) {
if (vis[sta[i].fir]||vis[sta[i].sec.fir]) goto jump;
// cout<<"add edge: "<<sta[i].fir<<' '<<sta[i].sec.fir<<' '<<sta[i].sec.sec<<endl;
vis[sta[i].fir]=vis[sta[i].sec.fir]=1;
val=val*sta[i].sec.sec%mod;
}
// cout<<"go dfs"<<endl;
dfs3(it.fir[0], 0);
for (int i=0; i<=sub[it.fir[0]]; ++i) h[i+__builtin_popcount(s)]=(h[i+__builtin_popcount(s)]+val*(dp[it.fir[0]][0][i]+dp[it.fir[0]][1][i]))%mod;
jump: ;
}
// cout<<"h: "; for (int i=0; i<=it.fir.size(); ++i) cout<<h[i]<<' '; cout<<endl;
}
void solve() {
for (int i=1; i<=xsiz; ++i) id1[i]=++tot;
for (int i=1; i<=ysiz; ++i) id2[i]=++tot;
for (int i=1; i<=k; ++i) {
to[id1[x[i]]].pb({id2[y[i]], w[i]-1});
to[id2[y[i]]].pb({id1[x[i]], w[i]-1});
}
for (int i=1; i<=xsiz; ++i) if (to[id1[i]].size() && !vis[id1[i]]) {
pair<vector<int>, vector<int>> tem;
dfs(id1[i], 0, tem);
sta.pb(tem);
}
g[0]=1;
for (auto& it:sta) {
// cout<<"it.fir: "; for (auto t:it.fir) cout<<t<<' '; cout<<endl;
// cout<<"it.sec: "; for (auto t:it.sec) cout<<t<<' '; cout<<endl;
if (it.fir.size()>it.sec.size()) swap(it.fir, it.sec);
int m=0;
for (auto& t:it.fir) m+=to[t].size();
if (it.fir.size()+it.sec.size()<0.666*m) solve1(it);
else solve2(it, m);
for (int i=0; i<=siz+it.fir.size(); ++i) t[i]=0;
for (int i=0; i<=siz; ++i)
for (int j=0; j<=it.fir.size(); ++j)
t[i+j]=(t[i+j]+g[i]*h[j])%mod; //, cout<<"add: "<<i+j<<' '<<g[i]<<' '<<h[j]<<endl;
siz+=it.fir.size();
for (int i=0; i<=siz; ++i) g[i]=t[i];
}
// cout<<"g: "; for (int i=0; i<=siz; ++i) cout<<g[i]<<' '; cout<<endl;
for (int i=0; i<=siz; ++i) ans=(ans+fac[n-i]*g[i])%mod;
printf("%lld\n", (ans%mod+mod)%mod);
}
}
signed main()
{
// freopen("field.in", "r", stdin);
// freopen("field.out", "w", stdout);
n=read(); k=read();
for (int i=1; i<=k; ++i) x[i]=read(), y[i]=read(), w[i]=read();
fac[0]=fac[1]=1;
for (int i=2; i<=n; ++i) fac[i]=fac[i-1]*i%mod;
for (int i=1; i<=k; ++i) {
unix[++xsiz]=x[i];
uniy[++ysiz]=y[i];
}
sort(unix+1, unix+xsiz+1);
sort(uniy+1, uniy+ysiz+1);
xsiz=unique(unix+1, unix+xsiz+1)-unix-1;
ysiz=unique(uniy+1, uniy+ysiz+1)-uniy-1;
for (int i=1; i<=k; ++i) {
x[i]=lower_bound(unix+1, unix+xsiz+1, x[i])-unix;
y[i]=lower_bound(uniy+1, uniy+ysiz+1, y[i])-uniy;
}
task2::solve();
return 0;
}