题意:
给出一棵树。设(E)表示边集,(V)表示点集,(S)为(V)的一个子集。
(f(S)=|(u,v)|(u,v)in E && uin V && vin V)。
求(displaystyle sum_{Ssubseteq V}f(S)^k)对(998244353)取模的结果。
$nleq100000,kleq 10 $。
还是对二项式定理不是很熟悉啊。
处理这类和的(k)次方问题经常用到二项式定理。
[displaystyle (a+b)^k=sum_{i=0}^kinom{k}{i}a^ib^{k-i}
]
于是对于每个(iin [0,k]),我们都维护(f_{v,i})表示(v)的子树中(displaystyle sum_{Ssubseteq T}f(S)^i)。
转移的时候就(k^2)转移就行了。
特别地,我们让(0^0=1),方便转移。
当然也可以用斯特林数
[displaystyle
n^m=sum_{i=0}^megin{Bmatrix}m\iend{Bmatrix}inom{n}{i}i!
]
我们有组合恒等式
[displaystyle
inom{a+b}{k}=sum_{i=0}^kinom{a}{i}inom{b}{k-i}
]
然后就可以同样的(k^2)。
代码:
#include<bits/stdc++.h>
#define ll long long
#define N 100005
using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
const ll mod=998244353;
ll ksm(ll t,ll x) {
ll ans=1;
for(;x;x>>=1,t=t*t%mod)
if(x&1) ans=ans*t%mod;
return ans;
}
int n,m,k;
struct road {
int to,nxt;
}s[N<<1];
int h[N],cnt;
void add(int i,int j) {s[++cnt]=(road) {j,h[i]};h[i]=cnt;}
ll f[N][2][15];
ll g[2][15],H[2][15];
ll c[15][15];
void dfs(int v,int fr) {
f[v][0][0]=f[v][1][0]=1;
for(int i=h[v];i;i=s[i].nxt) {
int to=s[i].to;
if(to==fr) continue ;
dfs(to,v);
memset(g,0,sizeof(g));
for(int j=0;j<=k;j++) {
for(int q=0;q<=k-j;q++) {
(g[0][j+q]+=c[j+q][j]*f[v][0][j]%mod*(f[to][0][q]+f[to][1][q]))%=mod;
(g[1][j+q]+=c[j+q][j]*f[v][1][j]%mod*f[to][0][q])%=mod;
}
}
memset(H,0,sizeof(H));
for(int j=0;j<=k;j++) {
for(int q=0;q<=k-j;q++) {
(H[1][j+q]+=c[j+q][j]*f[v][1][j]%mod*f[to][1][q])%=mod;
}
}
memcpy(f[v],g,sizeof(g));
for(int j=k;j>=1;j--)
for(int q=0;q<j;q++) (H[1][j]+=c[j][q]*H[1][q])%=mod;
for(int j=0;j<=k;j++) (f[v][1][j]+=H[1][j])%=mod;
}
}
int main() {
n=Get(),m=Get(),k=Get();
for(int i=0;i<=k;i++)
for(int j=0;j<=i;j++)
c[i][j]=(!j||i==j)?1:(c[i-1][j-1]+c[i-1][j])%mod;
int a,b;
for(int i=1;i<=m;i++) {
a=Get(),b=Get();
add(a,b),add(b,a);
}
dfs(1,0);
cout<<(f[1][0][k]+f[1][1][k])%mod;
return 0;
}