zoukankan      html  css  js  c++  java
  • BZOJ5093 [Lydsy1711月赛]图的价值 【第二类斯特林数 + NTT】

    题目链接

    BZOJ5093

    题解

    点之间是没有区别的,所以我们可以计算出一个点的所有贡献,然后乘上(n)
    一个点可能向剩余的(n - 1)个点连边,那么就有

    [ans = 2^{{n - 1 choose 2}}n sumlimits_{i = 0}^{n - 1} {n - 1 choose i} i^k ]

    显然要求

    [sumlimits_{i = 0}^{n} {n choose i} i^k ]

    然后我就不知道怎么做了。。

    翻翻题解
    有这样一个结论:

    [n^k = sumlimits_{i = 0}^{k} egin{Bmatrix} k \ i end{Bmatrix} {n choose i} i! ]

    那么就有

    [egin{aligned} sumlimits_{i = 0}^{n} {n choose i} i^k &= sumlimits_{i = 0}^{n} {n choose i} sumlimits_{j = 0}^{i} egin{Bmatrix} k \ j end{Bmatrix} {i choose j}j! \ &= sumlimits_{j = 0}^{n}egin{Bmatrix} k \ j end{Bmatrix} j! sumlimits_{i = j}^{n} {n choose i} {i choose j} \ &= sumlimits_{j = 0}^{n}egin{Bmatrix} k \ j end{Bmatrix} j! {n choose j} 2^{n - j} \ end{aligned} ]

    解释一下最后一步

    [sumlimits_{i = j}^{n} {n choose i} {i choose j} ]

    直观来看是从(n)中取出(i)个,然后从(i)中取出(j)
    实际上等价于从(n)中取出(j)个,剩余随便取

    最后只需要求出第二类斯特林数,用第二类斯特林反演即可

    [egin{aligned} egin{Bmatrix} n \ m end{Bmatrix} &= frac{1}{m!} sumlimits_{i = 0}^{m} (-1)^{i}{m choose i}(m - i)^{n} \ &= sumlimits_{i = 0}^{m} frac{(-1)^{i}}{i!} imes frac{(m - i)^{n}}{(m - i)!} \ end{aligned} ]

    (NTT)即可

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define mp(a,b) make_pair<int,int>(a,b)
    #define cls(s) memset(s,0,sizeof(s))
    #define cp pair<int,int>
    #define LL long long int
    using namespace std;
    const int maxn = 800005,maxm = 100005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    const int G = 3,P = 998244353;
    inline int qpow(int a,LL b){
    	int re = 1;
    	for (; b; b >>= 1,a = 1ll * a * a % P)
    		if (b & 1) re = 1ll * re * a % P;
    	return re;
    }
    int R[maxn];
    void NTT(int* a,int n,int f){
    	for (int i = 0; i < n; i++) if (i < R[i]) swap(a[i],a[R[i]]);
    	for (int i = 1; i < n; i <<= 1){
    		int gn = qpow(G,(P - 1) / (i << 1));
    		for (int j = 0; j < n; j += (i << 1)){
    			int g = 1,x,y;
    			for (int k = 0; k < i; k++,g = 1ll * g * gn % P){
    				x = a[j + k],y = 1ll * g * a[j + k + i] % P;
    				a[j + k] = (x + y) % P,a[j + k + i] = ((x - y) % P + P) % P;
    			}
    		}
    	}
    	if (f == 1) return;
    	int nv = qpow(n,P - 2); reverse(a + 1,a + n);
    	for (int i = 0; i < n; i++) a[i] = 1ll * a[i] * nv % P;
    }
    int fac[maxn],inv[maxn],fv[maxn],C[maxn];
    int S[maxn],B[maxn],N,K;
    void init(){
    	fac[0] = fac[1] = inv[0] = inv[1] = fv[0] = fv[1] = 1;
    	for (int i = 2; i <= K; i++){
    		fac[i] = 1ll * fac[i - 1] * i % P;
    		inv[i] = 1ll * (P - P / i) * inv[P % i] % P;
    		fv[i] = 1ll * fv[i - 1] * inv[i] % P;
    	}
    	C[0] = 1; int E = min(N - 1,K);
    	for (int i = 1; i <= E; i++) C[i] = 1ll * C[i - 1] * (N - i) % P * inv[i] % P;
    }
    void work(){
    	int n = 1,L = 0;
    	for (int i = 0; i <= K; i++){
    		S[i] = (((i & 1) ? -1 : 1) * fv[i] + P) % P;
    		B[i] = 1ll * qpow(i,K) * fv[i] % P;
    	}
    	while (n <= (K << 1)) n <<= 1,L++;
    	for (int i = 1; i < n; i++) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));
    	NTT(S,n,1); NTT(B,n,1);
    	for (int i = 0; i < n; i++) S[i] = 1ll * S[i] * B[i] % P;
    	NTT(S,n,-1);
    	//REP(i,10) printf("%d ",S[i]); puts("");
    	int ans = 0;
    	for (int i = 0; i < N; i++){
    		if (i > K) break;
    		ans = (ans + 1ll * S[i] * fac[i] % P * C[i] % P * qpow(2,N - 1 - i) % P) % P;
    	}
    	ans = 1ll * ans * N % P * qpow(2,1ll * (N - 1) * (N - 2) / 2) % P;
    	printf("%d
    ",ans);
    }
    int main(){
    	N = read(); K = read();
    	if (N == 1){puts("0"); return 0;}
    	if (N == 2){puts("2"); return 0;}
    	init();
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    Azure 虚拟机安全加固整理
    AzureARM 使用 powershell 扩容系统磁盘大小
    Azure Linux 云主机使用Root超级用户登录
    Open edX 配置 O365 SMTP
    powershell 根据错误GUID查寻错误详情
    azure 创建redhat镜像帮助
    Azure Powershell blob中指定的vhd创建虚拟机
    Azure Powershell 获取可用镜像 PublisherName,Offer,Skus,Version
    Power BI 连接到 Azure 账单,自动生成报表,可刷新
    Azure powershell 获取 vmSize 可用列表的命令
  • 原文地址:https://www.cnblogs.com/Mychael/p/9186590.html
Copyright © 2011-2022 走看看