BZOJ 3456: 城市规划 与 多项式求逆算法介绍(多项式求逆, dp)

题面

求有 n 个点的无向有标号连通图个数 . (1n1.3105)

题解

首先考虑 dp ... 直接算可行的方案数 , 容易算重复 .

我们用总方案数减去不可行的方案数就行了 (容斥)

fi 为有 i 个点的无向有标号连通图个数 .

考虑 1 号点的联通块大小 , 联通块外的点之间边任意 但 不能与 1 有间接联系 .

那么就有

fi=2(i2)j=1i1fj×(i1j1)×2(ij2)

这个可以直接用 CDQ分治FFT 求 , 但我不会 ...

那认真考虑一下这个式子 qwq

先展开组合数

fi=2(i2)j=1i1fj×(i1)!(j1)!(ij)!×2(ij2)

除去 (i1)!

fi(i1)!=2(i2)(i1)!j=1i1fj×2(ij2)(j1)!(ij)!

移项合并

j=1ifj×2(ij2)(j1)!×(ij)!=2(i2)(i1)!

左边我们观察一下不难发现是一个卷积形式 .

A=i=1nfi(i1)!xi

B=i=0n2(i2)i!xi

C=i=1n2(i2)(i1)!xi

就有 AB=CA=CB1

我们只需要求出 Bxn 下的逆元就行了 qwq

怎么求呢 :

多项式求逆 :

如果 BAxn 意义下的逆元 , 那么数学表达就是 :

AB1(modxn)

假设我们已经求出了Bxn2 的逆元 B . (我们常常令 n2k )

(1)AB1(modxn2)

我们之前那个 xn2 意义下也成立 ... 就有

(2)AB1(modxn2)

(2)(1) 就有

BB0(modxn2)

然后把它左右平方一下 (此处 xn2 也可平方成 xn )

B22BB+B20(modxn)

乘上一个 A 就得到

B2BAB2(modxn)

然后递归求解就行了 .

那么复杂度就是 T(n)=T(n2)+O(nlogn)=O(nlogn) ...

代码在这道题程序中有 ...

代码

/************************************************************** Problem: 3456 User: DOFY Language: C++ Result: Accepted Time:11956 ms Memory:107796 kb ****************************************************************/ #include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) using namespace std; typedef long long ll; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48); return x * fh; } void File() { #ifdef zjp_shadow freopen ("3456.in", "r", stdin); freopen ("3456.out", "w", stdout); #endif } const int N = (1 << 21) + 5, Mod = 1004535809; ll fpm(ll x, int power) { ll res = 1; for (; power; power >>= 1, (x *= x) %= Mod) if (power & 1) (res *= x) %= Mod; return res; } ll fac[N], ifac[N]; inline ll C(int n, int m) { if (n < 0 || m < 0 || n < m) return 0; return fac[n] * ifac[m] % Mod * ifac[n - m] % Mod; } void Init(int maxn) { fac[0] = ifac[0] = 1; For (i, 1, maxn) fac[i] = fac[i - 1] * i % Mod; ifac[maxn] = fpm(fac[maxn], Mod - 2); Fordown (i, maxn - 1, 1) ifac[i] = ifac[i + 1] * (i + 1) % Mod; } inline int Add(int a, int b) { return ((a += b) >= Mod) ? a - Mod : a; } struct Number_Theory_Transform { int pow3[N], invpow3[N]; inline void Init(int maxn) { for (int i = 2; i <= maxn; i <<= 1) pow3[i] = fpm(3, (Mod - 1) / i), invpow3[i] = fpm(pow3[i], Mod - 2); } int n, rev[N]; inline void Get_Rev() { int cnt = 0; for (int i = 1; i < n; i <<= 1) ++ cnt; For (i, 0, n - 1) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (cnt - 1)); } void NTT(int P[], int opt) { For (i, 0, n - 1) if (i < rev[i]) swap(P[i], P[rev[i]]); for (int i = 2; i <= n; i <<= 1) { int p = i >> 1, Wi = opt == 1 ? pow3[i] : invpow3[i]; for (int j = 0; j < n; j += i) for (int k = 0, x = 1; k < p; ++ k, x = 1ll * x * Wi % Mod) { int u = P[j + k], v = 1ll * x * P[j + k + p] % Mod; P[j + k] = Add(u, v); P[j + k + p] = Add(u, Mod - v); } } if (opt == -1) { int invn = fpm(n, Mod - 2); For (i, 0, n - 1) P[i] = 1ll * P[i] * invn % Mod; } } int A[N], B[N]; void Mult(int a[], int b[], int c[], int len) { for (n = 1; n <= len; n <<= 1); Get_Rev(); For (i, 0, n - 1) A[i] = a[i], B[i] = b[i]; NTT(A, 1); NTT(B, 1); For (i, 0, n - 1) A[i] = 1ll * A[i] * B[i] % Mod; NTT(A, -1); For (i, 0, n - 1) c[i] = A[i]; } void Get_Inv(int a[], int b[], int len) { if (len == 1) { b[0] = fpm(a[0], Mod - 2); return ; } Get_Inv(a, b, len >> 1); n = len << 1; Get_Rev(); For (i, 0, len - 1) A[i] = a[i], B[i] = b[i]; NTT(A, 1); NTT(B, 1); For (i, 0, n - 1) A[i] = 1ll * A[i] * B[i] % Mod * B[i] % Mod; NTT(A, - 1); For (i, 0, len - 1) b[i] = Add(Add(b[i], b[i]), Mod - A[i]); } } T; int a[N], b[N], c[N], tmp[N], n; int Edge(int x, int nowmod) { return 1ll * x * (x - 1) / 2 % nowmod; } int main () { File(); n = read(); T.Init(1 << 20); Init(n); For (i, 0, n) b[i] = fpm(2, Edge(i, Mod - 1)) * ifac[i] % Mod; For (i, 0, n) c[i] = fpm(2, Edge(i, Mod - 1)) * ifac[i - 1] % Mod; int len = 1; for (; len <= n; len <<= 1); T.Get_Inv(b, tmp, len); T.Mult(tmp, c, a, len); printf ("%lld\n", 1ll * a[n] * fac[n - 1] % Mod); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9163413.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(431)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示