第二类斯特林数【清华冬令营2018模拟】送你一个DAG

题目

1.1 Description
送你一个n 个点m 条边的DAG 和参数k, 定义一条经过l 条边的路径的权值为l^k.
对于i=1…n, 求出所有1 到i 的路径的权值之和, 对998244353 取模.
1.2 Input Format
第一行三个整数n; m; k, 分别表示DAG 的点数, 边数和参数.
接下来m 行, 每行两个整数ui; vi, 表示一条从ui 到vi 的有向边.
1.3 Output Format
共输出n 行, 第i 行一个整数, 表示i 号点的答案.
1.4 Sample
1.4.1 Input
6 8 2
1 2
1 3
1 5
2 4
3 2
3 4
3 6
4 6
1.4.2 Output
0
5
1
17
1
38
1.5 Constraints
对于前20% 的数据, n 2000;m 5000;
对于另10% 的数据, k = 1;
对于另20% 的数据, k =30;
对于100% 的数据, 1 n 100000; 1 m 200000; 0 k 500, 保证从1 出发可以到达每
个点, 可能会有重边.

题解

发现k的值比较小,那么不妨设f[i][x]表示到第i个点所有路径的x次方的和,使用二项式定理可以搞到nk2nk2

但是现在要搞到nk

f[i][x]=len(i)xf[i][x]=∑len(i)x
这里需要使用第二类斯特林树
第二类斯特林树表示的是把n个有特征的球放入m个没有特征的盒子的方案数(每一个盒子里面都至少有一个球),一般也可以用{nmmn}或s[n][m]来表示

考虑一下mnmn的数学意义,就是把n个本质不同的数放到m个不同的盒子里面,这就等于选出x(x=1..m)个盒子然后再放球的总方案数,即:mn=mi=1s[n][i]mimn=∑i=1ms[n][i]∗m−i,也就是先一个排列选出要的盒子,再一个第二类斯特林数

那么套进去这一题里面呢?

len(p)k=ki=1s[k][i]len(p)ilen(p)k=∑i=1ks[k][i]∗len(p)−i
最后一段是一个排列,我们把它除上一个j!让它变成一个组合数
就是
ki=1s[k][i]i!Cilen(p)∑i=1ks[k][i]∗i!∗Clen(p)i
其中len(p)表示一条长度为p的路径
那么我们可以设f[x][i]=Cilen(p)f[x][i]=∑Clen(p)i,这个可以有杨奎三角定义O(1)转移
然后剩下的问题就是求s[n][m]了
考虑使用递推,s[n][m]=s[n-1][m]*m+s[n-1][m-1]
第一段表示新加进来的一段加到一个原来就有的集合里面,后面的那一段表示我把新加入的数放到了一个新的集合里面

这样就完美解决本题了~

贴代码

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ll long long
using namespace std;

const int maxn=1e5+5,md=998244353;

ll f[maxn][505],g[505][505];
int fi[maxn],ne[maxn*2],dui[maxn*2],qc[maxn];
int ru[maxn],h[maxn];
int i,j,k,l,m,n,x,y,now,p,z;
ll ans,s;

void add(int x,int y){
    if (fi[x]==0) fi[x]=++now; else ne[qc[x]]=++now;
    dui[now]=y; qc[x]=now;
}
int main(){
    freopen("t1.in","r",stdin);
    freopen("t1.out","w",stdout);
    scanf("%d%d%d",&n,&m,&p);
    fo(i,1,m){
        scanf("%d%d",&x,&y); add(x,y); ru[y]++;
    }
    h[1]=1; i=1; j=0;
    f[1][0]=1;  
    while (i>j){
        x=h[++j];
        k=fi[x];
        while (k){
            f[dui[k]][0]=(f[dui[k]][0]+f[x][0])%md;
            fo(z,1,p) f[dui[k]][z]=(f[dui[k]][z]+(f[x][z]+f[x][z-1])%md)%md;
            ru[dui[k]]--;
            if (!ru[dui[k]]) h[++i]=dui[k];
            k=ne[k];
        }
    }
    g[0][0]=1;
    fo(i,1,p)
        fo(j,0,p){
            g[i][j]=(1ll*j*g[i-1][j])%md;
            if (j) g[i][j]=(g[i][j]+g[i-1][j-1])%md;
        }
    fo(i,1,n){
        s=1; ans=0;
        fo(j,1,p){
            if (j) s=(s*j)%md;
            ans=(ans+((s*g[p][j])%md)*f[i][j])%md;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

你可能感兴趣的:(数学,斯特林数)