第二類斯特林數【清華冬令營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<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章