P4841 城市规划
题目描述#
刚刚解决完电力网络的问题,阿狸又被领导的任务给难住了。
刚才说过,阿狸的国家有 个城市,现在国家需要在某些城市对之间建立一些贸易路线,使得整个国家的任意两个城市都直接或间接的连通。
为了省钱, 每两个城市之间最多只能有一条直接的贸易路径。对于两个建立路线的方案,如果存在一个城市对,在两个方案中是否建立路线不一样,那么这两个方案就是不同的,否则就是相同的。现在你需要求出一共有多少不同的方案。
好了,这就是困扰阿狸的问题。换句话说,你需要求出 个点的简单 (无重边无自环) 有标号无向连通图数目。
由于这个数字可能非常大, 你只需要输出方案数对 即可。
数据范围: 。
solution#
多项式的经典题了。
首先设 表示 个点有标号无向连通图的个数, 表示 个点的无向图(可以不连通)的个数, 根据小学知识可得: ,则有:
这个可以怎么理解呢?我们可以枚举 所在的联通块的个数 , 首先要从剩下的 个点里面选 个点来构成这个联通块,方案数即为 。然后这 个点组成的有标号无向连通图的个数为 , 剩下的 个点随便连,方案数为 。然后就有了我们上面的那个柿子。
我们尝试把组合数拆开来构成卷积则有:
把 提出来移到左边可得:
上面的柿子是个很明显的卷积式,考虑构造多项式 其中:
则有 ,那么 。
我们求出 的乘法逆,在卷上 就可以得到 每一项的系数。
最后的答案即为 。
坑点:注意多项式的求逆操作要保证多项式的项数在 的整次幂的时候进行,(我在这里卡了好久)。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
const int N = 5e5+10;
const int p = 1004535809;
int n,rev[N],jz[N],inv[N],a[N],b[N],c[N],d[N],base[N];
inline int read()
{
int s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
int ksm(int a,int b)
{
int res = 1;
for(; b; b >>= 1)
{
if(b & 1) res = res * a % p;
a = a * a % p;
}
return res;
}
void NTT(int *a,int lim,int opt)
{
for(int i = 0; i < lim; i++)
{
if(i < rev[i]) swap(a[i],a[rev[i]]);
}
for(int h = 1; h < lim; h <<= 1)
{
int wn = ksm(3,(p-1)/(h<<1));
if(opt == -1) wn = ksm(wn,p-2);
for(int j = 0; j < lim; j += (h<<1))
{
int w = 1;
for(int k = 0; k < h; k++)
{
int u = a[j + k];
int v = w * a[j + h + k] % p;
a[j + k] = (u + v) % p;
a[j + h + k] = (u - v + p) % p;
w = w * wn % p;
}
}
}
if(opt == -1)
{
int INV = ksm(lim,p-2);
for(int i = 0; i < lim; i++) a[i] = a[i] * INV % p;
}
}
void Inv(int *a,int *b,int n)
{
if(n == 1)
{
b[0] = ksm(a[0],p-2);
return;
}
Inv(a,b,(n+1)>>1);
int lim = 1, tim = 0;
while(lim < (n<<1)) lim <<= 1, tim++;
for(int i = 0; i < lim; i++) rev[i] = (rev[i>>1]>>1) | ((i&1)<<(tim-1));
for(int i = 0; i < n; i++) c[i] = a[i];
for(int i = n; i < lim; i++) c[i] = 0;
NTT(c,lim,1); NTT(b,lim,1);
for(int i = 0; i < lim; i++) b[i] = (2 * b[i] % p - c[i] * b[i] % p * b[i] % p + p) % p;
NTT(b,lim,-1);
for(int i = n; i < lim; i++) b[i] = 0;
}
signed main()
{
n = read();
jz[0] = inv[0] = 1; base[0] = 1;
for(int i = 1; i <= n; i++) jz[i] = jz[i-1] * i % p;
inv[n] = ksm(jz[n],p-2);
for(int i = n-1; i >= 1; i--) inv[i] = inv[i+1] * (i+1) % p;
for(int i = 1; i <= n; i++) base[i] = ksm(2,i*(i-1)/2);
for(int i = 1; i <= n; i++) a[i] = base[i] * inv[i-1] % p;
for(int i = 0; i <= n; i++) d[i] = base[i] * inv[i] % p;
int len = 1;
while(len <= n) len <<= 1;
Inv(d,b,len);
int lim = 1, tim = 0;
while(lim < (n<<1)) lim <<= 1, tim++;
for(int i = 0; i < lim; i++) rev[i] = (rev[i>>1]>>1) | ((i&1)<<(tim-1));
NTT(a,lim,1); NTT(b,lim,1);
for(int i = 0; i < lim; i++) a[i] = a[i] * b[i] % p;
NTT(a,lim,-1);
printf("%lld\n",a[n]*jz[n-1]%p);
return 0;
}
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 开发者新选择:用DeepSeek实现Cursor级智能编程的免费方案
· Tinyfox 发生重大改版
· 独立开发经验谈:如何通过 Docker 让潜在客户快速体验你的系统
· 小米CR6606,CR6608,CR6609 启用SSH和刷入OpenWRT 23.05.5
· 近期最值得关注的AI技术报告与Agent综述!