CF468E Permanent
https://www.luogu.com.cn/problem/CF468E
不会做,差点把心态搞没
考虑积和式的组合意义
可以看做是一个二分图的所有的完美匹配的边权乘积的和
考虑把每条边\(w\)拆成\(1\)和\(w-1\),那么对于\(w-1\)的的边构成的二分图,只需要找它的任意一个匹配,然后直接乘上未匹配的点的阶乘即可(剩下的点随意匹配)。状压\(DP\)即可,考虑优化,注意到如果某一列后面都不会再用到了,可以不状压这一列,分析可得最坏可以被卡到
\(O(m^22^{\frac{m}{2}})\)
因为换一种行的枚举顺序貌似就卡不掉了,所以没有特意去卡
官方题解是根号分治的做法,但实际跑起来并不比上述做法跑得快。
code:
#include<bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define mod 1000000007
#define N 2000050
using namespace std;
struct {
int u, v, c;
} a[N];
int n, m, bu[N], bv[N], n1, n2;
ll fac[N], ok[N];
map<ll, ll> f[105][105];
vector<pair<int, int> > e[N];
int main() {
scanf("%d%d", &n, &m);
fac[0] = 1;
for(int i = 1; i <= n; i ++) fac[i] = fac[i - 1] * i % mod;
for(int i = 1; i <= m; i ++) {
scanf("%d%d%d", &a[i].u, &a[i].v, &a[i].c); a[i].c = (a[i].c - 1 + mod) % mod;
bu[++ n1] = a[i].u, bv[++ n2] = a[i].v;
}
sort(bu + 1, bu + 1 + n1), n1 = unique(bu + 1, bu + 1 + n1) - bu - 1;
sort(bv + 1, bv + 1 + n2), n2 = unique(bv + 1, bv + 1 + n2) - bv - 1;
for(int i = 1; i <= m; i ++) {
a[i].u = lower_bound(bu + 1, bu + 1 + n1, a[i].u) - bu;
a[i].v = lower_bound(bv + 1, bv + 1 + n2, a[i].v) - bv;
e[a[i].u].push_back(make_pair(a[i].v, a[i].c));
}
for(int i = n1; i >= 1; i --) {
ok[i] = ok[i + 1];
for(auto x : e[i]) ok[i] |= (1ll << x.fi);
}
f[0][0][0] = 1;
for(int i = 0; i < n1; i ++)
for(int j = 0; j <= i; j ++)
for(auto x : f[i][j]) {
(f[i + 1][j][x.fi & ok[i + 1]] += x.se) %= mod;
if(x.se)
for(auto y : e[i + 1]) if(!((x.fi >> y.fi) & 1) && y.se) {
(f[i + 1][j + 1][(x.fi | (1ll << y.fi)) & ok[i + 1]] += 1ll * y.se * x.se % mod) %= mod;
}
}
ll ans = 0;
for(int i = 0; i <= n1; i ++)
for(auto x : f[n1][i])
ans = (ans + 1ll * x.se * fac[n - i] % mod) % mod;//, printf("%lld %lld %lld\n", x.fi, x.se, fac[n - i]);
printf("%lld", ans);
return 0;
}