題目
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]∗mi−mn=∑i=1ms[n][i]∗m−i,也就是先一個排列選出要的盒子,再一個第二類斯特林數
那麼套進去這一題裏面呢?
len(p)k=∑ki=1s[k][i]∗len(p)i−len(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;
}