题解:P7718 「EZEC-10」Equalization
_Communist · · 题解
竞选最 tang 做法。
首先
直接套
既然这个第一问阻碍了无脑套
再考虑第二问,为了在满足第一问的条件下计数。
假设已经知道了
至于如何求
-
设最优方案中选了
c 个和为0 的集合,再令F_S=|S|^{|S|-2} 当且仅当S 和为0 ,则相当于求F^c (这里集合幂级数之间乘法为子集卷积)。 -
延续
\exp 的思路,令F_S=|S|^{|S|-2} 当且仅当S 和为0 且S 极小,则f=\exp F 。极小的意思是不存在T\subsetneqq S 满足T 和为0 ,如果存在的话S 必然不会在最优解里面。
第一种做法常数应该很大,代码实现了第二种做法,常数稍微小一点。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using LL = unsigned long long;
constexpr int MAXN = 22, mod = 1e9 + 7, INF = 1e9;
inline void inc(int &x, int y) { (x += y) >= mod && (x -= mod); }
inline void dec(int &x, int y) { (x -= y) < 0 && (x += mod); }
inline int qpow(int x, int y = mod - 2) {
int ret = 1;
while (y) {
if (y & 1) ret = (LL)ret * x % mod;
x = (LL)x * x % mod, y >>= 1;
}
return ret;
}
int n, inv[MAXN + 1], cl[MAXN + 1], excl[MAXN + 1], popc[1 << MAXN];
long long a[MAXN + 1], s[1 << MAXN];
int f[1 << MAXN], dp[1 << MAXN], F[MAXN + 1][1 << MAXN], G[MAXN + 1][1 << MAXN];
inline void FMT(int *f) { for (int i = 0; i < n; ++i) for (int S = 0; S < (1 << n); ++S) if ((S >> i) & 1) inc(f[S], f[S ^ (1 << i)]); }
inline void IFMT(int *f) { for (int i = 0; i < n; ++i) for (int S = 0; S < (1 << n); ++S) if ((S >> i) & 1) dec(f[S], f[S ^ (1 << i)]); }
inline void exp() {
for (int S = 0; S < (1 << n); ++S) F[popc[S]][S] = dp[S];
for (int i = 0; i <= n; ++i) FMT(F[i]);
for (int i = 0; i <= n; ++i) for (int S = 0; S < (1 << n); ++S) G[i][S] = (LL)F[i][S] * i % mod;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j < i; ++j) {
for (int S = 0; S < (1 << n); ++S) inc(G[i][S], (LL)G[j][S] * F[i - j][S] % mod * (i - j) % mod);
}
for (int S = 0; S < (1 << n); ++S) G[i][S] = (LL)G[i][S] * inv[i] % mod;
}
for (int i = 0; i <= n; ++i) IFMT(G[i]);
for (int S = 0; S < (1 << n); ++S) dp[S] = G[popc[S]][S];
}
inline void init() {
for (int i = 1; i <= n; ++i) inv[i] = qpow(i);
cl[0] = cl[1] = 1;
for (int i = 2; i <= n; ++i) cl[i] = qpow(i, i - 2);
excl[0] = 1;
for (int i = 1; i <= n; ++i) excl[i] = (LL)qpow(i + 2, i - 1) % mod * 2 % mod;
for (int S = 1; S < (1 << n); ++S) s[S] = s[S ^ (S & -S)] + a[__builtin_ctz(S)], popc[S] = __builtin_popcount(S);
}
int main() {
IOS;
cin >> n;
for (int i = 0; i < n; ++i) cin >> a[i];
--n;
for (int i = 0; i < n; ++i) a[i] = a[i + 1] - a[i];
init();
fill(f + 1, f + (1 << n), INF);
for (int S = 0; S < (1 << n); ++S) {
for (int i = 0; i < n; ++i) if ((S >> i) & 1) f[S] = min(f[S], f[S ^ (1 << i)] + bool(s[S]));
}
cout << f[(1 << n) - 1] << '\n';
for (int S = 0; S < (1 << n); ++S) if (!s[S] && f[S] == popc[S] - 1 && f[((1 << n) - 1) ^ S] + f[S] == f[(1 << n) - 1]) dp[S] = cl[popc[S]];
exp();
dp[0] = 1;
int ans = 0;
for (int S = 0; S < (1 << n); ++S) if (!s[S] && f[S] + n - popc[S] == f[(1 << n) - 1]) {
inc(ans, (LL)dp[S] * excl[n - popc[S]] % mod);
}
for (int i = 1; i <= f[(1 << n) - 1]; ++i) ans = (LL)ans * i % mod;
cout << ans << '\n';
return 0;
}