bzoj5093: [Lydsy1711月赛]图的价值(第二类斯特林数)

这题貌似是一样的啊
传送门
题意:
对于一张无向图,它的权值是所有点的权值和,一个点权值是它度数的
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=998244353;
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(3,(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 ret,n,m;
int main(){
    init();
    ntt_init();
    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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值