BZOJ 5093: 图的价值 第二类斯特林数 NTT

奋斗吧
奋斗吧
擅长邻域:未填写

标签: BZOJ 5093: 图的价值 第二类斯特林数 NTT HarmonyOS博客 51CTO博客

2023-07-07 18:24:22 302浏览

BZOJ 5093: 图的价值 第二类斯特林数 NTT,5093:图的价值TimeLimit: 30Sec  MemoryLimit: 256MBSubmit: 176  Solved: 89[Submit][Status][Discuss]Description“简单无向图”是指无重边、



5093: 图的价值


Time Limit: 30 Sec  Memory Limit: 256 MB

Submit: 176  Solved: 89

[Submit][Status][Discuss]

Description


“简单无向图”是指无重边、无自环的无向图(不一定连通)。



一个带标号的图的价值定义为每个点度数的k次方的和。



给定n和k,请计算所有n个点的带标号的简单无向图的价值之和。



因为答案很大,请对998244353取模输出。



Input


第一行包含两个正整数n,k(1<=n<=10^9,1<=k<=200000)。



Output


 输出一行一个整数,即答案对998244353取模的结果。



Sample Input


6 5


Sample Output


67584000



这是一篇超详尽的题解:

 

题意求n个点组成的所有简单无向图的所有点的度数k次方之和

 

BJ 连最开始的式子都没写出来 太弱弱弱弱了 泪

考虑 因为要计算所有情况 则每个点的贡献是相同的

// 第一步没想到 太太太弱了

那么接下来只要枚举度数 并计算有多少图满足条件即可

BZOJ 5093: 图的价值 第二类斯特林数 NTT_斯特林数

这样 我们就可以考虑开始对它进行化简了

// 为了方便书写 提出来的常数会忽略

一般对于一个k次方求和的形式我们都可以利用第二类斯特林数的性质化简

BZOJ 5093: 图的价值 第二类斯特林数 NTT_#include_02

所以这个式子就化为

BZOJ 5093: 图的价值 第二类斯特林数 NTT_无向图_03

这时候式子较为复杂 化简方式也不止一种

比如说 是否改变枚举顺序 是否用通项拆开斯特林数 是否把组合数拆成阶乘

仅考虑这三点 我们就会有8种化简方法

但是一定是可以找到最优的化简方案的

首先 这个题的n看起来很不好处理范围是1e9

这一定是不允许枚举的 卷积也搞不了他

这时 唯一的解决方法就是利用斯特林数的枚举上限是min(n,K)进行限制

那么我们改变枚举顺序

BZOJ 5093: 图的价值 第二类斯特林数 NTT_无向图_04

之后 考虑斯特林数的处理

如果拆开式子就又会多一重枚举 就不好压掉了

而且K是定值一行斯特林数可以卷积卷出来

那么先不拆

I! 是常数 姑且先提出来

BZOJ 5093: 图的价值 第二类斯特林数 NTT_斯特林数_05

现在就要思考如何压掉deg的枚举了

这个时候就出现了一个高级操作

我们考虑

BZOJ 5093: 图的价值 第二类斯特林数 NTT_斯特林数_06

的意义!!!把式子倒过来

BZOJ 5093: 图的价值 第二类斯特林数 NTT_#include_07

 n-1个里选deg个 deg个里再选i个

等价于 n-1个里选i个 剩下的随便选!!


BZOJ 5093: 图的价值 第二类斯特林数 NTT_无向图_08

这样一来 便化腐朽为神奇

它摇身一变

BZOJ 5093: 图的价值 第二类斯特林数 NTT_#include_09

这样就可以直接枚举啦

Yeah


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
using namespace std;

typedef long long ll;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=525000,mod=998244353;

inline int qpow(int x,int y)
{
	int res(1);
	while(y)
	{
		if(y&1) res=1ll*res*x%mod;
		x=1ll*x*x%mod;
		y>>=1;
	}
	return res;
}

int r[N];

void ntt(int *x,int lim,int opt)
{
	register int i,j,k,m,gn,g,tmp;
	for(i=0;i<lim;++i)
	{
		r[i]=(i&1)*(lim>>1)+(r[i>>1]>>1);
		if(r[i]<i) swap(x[i],x[r[i]]);
	}
	for(m=2;m<=lim;m<<=1)
	{
		k=m>>1;
		gn=qpow(3,(mod-1)/m);
		for(i=0;i<lim;i+=m)
		{
			g=1;
			for(j=0;j<k;++j,g=1ll*g*gn%mod)
			{
				tmp=1ll*x[i+j+k]*g%mod;
				x[i+j+k]=(x[i+j]-tmp+mod)%mod;
				x[i+j]=(x[i+j]+tmp)%mod;
			}
		}
	}
	if(opt==-1)
	{
		reverse(x+1,x+lim);
		int inv(qpow(lim,mod-2));
		for(i=0;i<lim;++i) x[i]=1ll*x[i]*inv%mod;
	}
}

int A[N],B[N],S[N];

int fac[N],inv[N];

int main()
{
	int n=read(),K=read();
	register int i,j,lim(1);
	fac[0]=1;for(i=1;i<=K;++i) fac[i]=1ll*fac[i-1]*i%mod;
	inv[1]=1;for(i=2;i<=K;++i) inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
	inv[0]=1;for(i=1;i<=K;++i) inv[i]=1ll*inv[i-1]*inv[i]%mod;
	
	for(i=0;i<=K;++i) A[i]=i&1 ? mod-inv[i] : inv[i];
	for(i=0;i<=K;++i) B[i]=1ll*qpow(i,K)*inv[i]%mod;
	
	while(lim<=(K<<1)) lim<<=1;
	ntt(A,lim,1);ntt(B,lim,1);
	for(i=0;i<lim;++i) S[i]=1ll*A[i]*B[i]%mod;
	ntt(S,lim,-1);
	
	int ans(0);
	for(i=0,j=1;i<=min(n-1,K);++i,j=1ll*j*(n-i)%mod)
		(ans+=1ll*S[i]*j%mod*qpow(2,n-i-1)%mod)%=mod;
	ans=1ll*ans*n%mod;
	ans=1ll*ans*qpow(2,(1ll*(n-1)*(n-2)>>1)%(mod-1))%mod;
	cout<<ans<<endl;
	return 0;
}



好博客就要一起分享哦!分享海报

此处可发布评论

评论(0展开评论

暂无评论,快来写一下吧

展开评论

您可能感兴趣的博客

客服QQ 1913284695