• P4841 城市规划


    题目描述

    洛谷

    刚刚解决完电力网络的问题,阿狸又被领导的任务给难住了。

    刚才说过,阿狸的国家有 (n) 个城市,现在国家需要在某些城市对之间建立一些贸易路线,使得整个国家的任意两个城市都直接或间接的连通。

    为了省钱, 每两个城市之间最多只能有一条直接的贸易路径。对于两个建立路线的方案,如果存在一个城市对,在两个方案中是否建立路线不一样,那么这两个方案就是不同的,否则就是相同的。现在你需要求出一共有多少不同的方案。

    好了,这就是困扰阿狸的问题。换句话说,你需要求出 (n) 个点的简单 (无重边无自环) 有标号无向连通图数目。

    由于这个数字可能非常大, 你只需要输出方案数对 (1004535809 ( 479 imes 2 ^{21} + 1 )) 即可。

    数据范围:(nleq 130000)

    solution

    多项式的经典题了。

    首先设 (f(i)) 表示 (n) 个点有标号无向连通图的个数,(g(i)) 表示 (i) 个点的无向图(可以不连通)的个数, 根据小学知识可得:(g(n) = 2^{nchoose 2}) ,则有:

    (g(n) = displaystylesum_{i=1}^{n} f(i) imes {n-1choose i-1} imes g(n-i))

    这个可以怎么理解呢?我们可以枚举 (1) 所在的联通块的个数 (i), 首先要从剩下的 (n-1) 个点里面选 (i-1) 个点来构成这个联通块,方案数即为 (n-1choose i-1) 。然后这 (i) 个点组成的有标号无向连通图的个数为 (f(i)), 剩下的 (n-i) 个点随便连,方案数为 (g(n-i)) 。然后就有了我们上面的那个柿子。

    我们尝试把组合数拆开来构成卷积则有:

    (g(n) = displaystylesum_{i=1}^{n} f(i) imes {(n-1)!over (i-1)!(n-i)!} imes g(n-i))

    ((n-1)!) 提出来移到左边可得:

    (displaystyle {g(n)over (n-1)!} = displaystylesum_{i=1}^{n} {f(i)over (i-1)!} imes {g(n-i)over (n-i)!})

    上面的柿子是个很明显的卷积式,考虑构造多项式 (A(x),B(x),C(x)) 其中:

    (displaystyle A(x) = displaystylesum_{i=0}^{n} {g(i)over (n-1)!} x^i)

    (displaystyle B(x) = sum_{i-0}^{n} {f(i)over (i-1)!} x^i)

    (C(x) = displaystylesum_{i=0}^{n} {g(i)over i!} x^i)

    则有 (A(x) = B(x) * C(x)) ,那么 (B(x) = C(x) * A(x)^{-1})

    我们求出 (A(x)) 的乘法逆,在卷上 (C(x)) 就可以得到 (B(x)) 每一项的系数。

    最后的答案即为 ([x^n] B(x) imes (n-1)!)

    坑点:注意多项式的求逆操作要保证多项式的项数在 (2) 的整次幂的时候进行,(我在这里卡了好久)。

    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
    ",a[n]*jz[n-1]%p);
    	return 0;
    }
    
  • 相关阅读:
    java web乱码及解决方法
    同时启动多个tomcat,端口修改
    oracle RAC LOG_ARCHIVE_DEST_1 与 LOG_ARCHIVE_DEST 冲突解决
    Oracle RAC 集群启动与停止
    Oracle 存储过程批量插入数据
    本地NTP服务器与客户端配置
    oracle 正确删除归档日志,并清除 V$ARCHIVED_LOG 数据
    oracle 断电启动失败:ORA-00600: internal error code, arguments
    maven 打包并导出 lib 第三方jar
    利用MAVEN打包可运行jar包,包括依赖的第三方包
  • 原文地址:https://www.cnblogs.com/genshy/p/14521685.html
Copyright © 2020-2023  润新知