HackerRank costly graphs(第二类斯特林数)

传送门
题意:
对于一张无向图,它的权值是所有点的权值和,一个点权值是它度数的
m 次方,问所有 n 个点简单无向图的权值和。
n ≤ 1 e 9 , m ≤ 2 e 5 n ≤ 1e9, m ≤ 2e5 n1e9,m2e5
思路:
显然每个点在所有图中的贡献是一样的。
于是我们钦定一个点 i i i并计算它在所有图中的贡献 f i f_i fi,这个贡献可以通过枚举它的度来计算:
f x = 2 C n − 1 2 ∑ i = 0 n − 1 C n − 1 i i m f_x=2^{C_{n-1}^2}\sum_{i=0}^{n-1}C_{n-1}^ii^m fx=2Cn12i=0n1Cn1iim
前面的 2 C n − 1 2 2^{C_{n-1}^2} 2Cn12表示的是剩余 n − 1 n-1 n1个点在所有图中对应的连通情况,后面的 C n − 1 i i m C_{n-1}^ii^m Cn1iim相当于是在剩余 n − 1 n-1 n1个点中选出来 i i i个跟 x x x连边,于是 x x x的贡献就统计出来了。
由于每个点的贡献是相同的,所以总贡献为 n ∗ 2 C n − 1 2 ∑ i = 0 n − 1 C n − 1 i i m n*2^{C_{n-1}^2}\sum_{i=0}^{n-1}C_{n-1}^ii^m n2Cn12i=0n1Cn1iim
考虑将这个式子进行变形:
a n s = n 2 C n − 1 2 ∑ i = 0 n − 1 C n − 1 i i m ans=n2^{C_{n-1}^2}\sum_{i=0}^{n-1}C_{n-1}^ii^m ans=n2Cn12i=0n1Cn1iim
a n s = n 2 C n − 1 2 ∑ i = 0 n − 1 C n − 1 i ∑ j = 0 m S m j i j ‾ ans=n2^{C_{n-1}^2}\sum_{i=0}^{n-1}C_{n-1}^i\sum_{j=0}^mS_{m}^ji^{\underline j} ans=n2Cn12i=0n1Cn1ij=0mSmjij
然后更换枚举顺序:
a n s = n 2 C n − 1 2 ∑ i = 0 m S m i ∑ j = 0 n − 1 j i ‾ C n − 1 j ans=n2^{C_{n-1}^2}\sum_{i=0}^mS_{m}^i\sum_{j=0}^{n-1}j^{\underline i}C_{n-1}^j ans=n2Cn12i=0mSmij=0n1jiCn1j
然后对最后一个 ∑ \sum 里的东西考虑一下组合意义:意思应该是从 n − 1 n-1 n1个中选 j j j个出来,在从 j j j个中选 i i i个出来排列。
这个等价于从 n − 1 n-1 n1个中选 i i i个出来排列,剩下 n − i − 1 n-i-1 ni1个可以选可以不选。
于是可以把式子继续变形:
a n s = n 2 C n − 1 2 ∑ i = 0 m S m i ( n − 1 ) i ‾ 2 n − i − 1 ans=n2^{C_{n-1}^2}\sum_{i=0}^mS_{m}^i(n-1)^{\underline i}2^{n-i-1} ans=n2Cn12i=0mSmi(n1)i2ni1
然后用 n t t ntt ntt预处理第二类斯特林数即可。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
typedef long long ll;
const int mod=1005060097;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=200005,up=200000;
int fac[N],ifac[N];
inline void init(){
	fac[0]=fac[1]=ifac[0]=ifac[1]=1;
	for(ri i=2;i<=up;++i)fac[i]=mul(fac[i-1],i),ifac[i]=mul(ifac[mod-mod/i*i],mod-mod/i);
	for(ri i=2;i<=up;++i)Mul(ifac[i],ifac[i-1]);
}
vector<int>w[20],pos[20];
int invv[20];
const int Lim=1<<20;
inline void ntt_init(){
	for(ri tt=1,g,mt=1,inv=(mod+1)>>1,t=0;tt<Lim;tt<<=1,++t,Mul(mt,inv)){
		w[t].resize(tt),pos[t].resize(tt);
		w[t][0]=1,g=ksm(5,(mod-1)/(tt<<1));
		invv[t]=mt;
		for(ri i=1;i<tt;++i)w[t][i]=mul(w[t][i-1],g),pos[t][i]=(pos[t][i>>1]>>1)|((i&1)<<(t-1));
	}
}
int lim,tim;
vector<int>a,b;
inline void Init(const int&up){lim=1,tim=0;while(lim<=up)lim<<=1,++tim;}
inline void ntt(vector<int>&a,int type){
	for(ri i=0;i<lim;++i)if(i<pos[tim][i])swap(a[i],a[pos[tim][i]]);
	for(ri i=1,a0,a1,t=0;i<lim;i<<=1,++t){
		for(ri j=0,len=i<<1;j<lim;j+=len){
			for(ri k=0;k<i;++k){
				a0=a[j+k],a1=mul(w[t][k],a[j+k+i]);
				a[j+k]=add(a0,a1),a[j+k+i]=dec(a0,a1);
			}
		}
	}
	if(~type)return;
	reverse(++a.begin(),a.end());
	for(ri i=0;i<lim;++i)Mul(a[i],invv[tim]);
}
int main(){
	init();
	ntt_init();
	for(ri ret,n,m,tt=read();tt;--tt){
		n=read(),m=read();
		ret=0;
		a.resize(m+1),b.resize(m+1);
		for(ri i=0;i<=m;++i)a[i]=mul(ifac[i],ksm(i,m)),b[i]=i&1?mod-ifac[i]:ifac[i];
		Init(m<<1);
		a.resize(lim),b.resize(lim);
		for(ri i=m+1;i<lim;++i)a[i]=b[i]=0;
		ntt(a,1),ntt(b,1);
		for(ri i=0;i<lim;++i)Mul(a[i],b[i]);
		ntt(a,-1);
		for(ri i=0,mt=1,det=n;i<=m;++i,Mul(mt,--det)){
			if(i==n)break;
			Add(ret,mul(a[i],mul(mt,ksm(2,n-i-1))));
		}
		Mul(ret,mul(n,ksm(2,(ll)(n-1)*(n-2)/2%(mod-1))));
		cout<<ret<<'\n';
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值