[GDOI2018]滑稽子图

题目链接:【被和谐】

题目大意:对于一棵树(V,E),对于SVf(S)为点集S的导出子图的边数。求SVf(S)k

这里的导出子图说的是,点集为S,边集为{(u,v)E|u,vS}的一个子图。


看到这个k次方,马上用斯特林数。

ans=SVf(S)k=i=0ki!S(k,i)SV(f(S)i)

然后考虑怎么求后面那个式子。

这个式子表示在S的导出子图里面选i条边的方案数,然后就可以树形dp了

dpx,s,0/1表示在以x为根的子树内部,选择s条边,x是否S的答案。

在新加上一个x的子树v的时候,S只有原来只有新的子树的情况直接加上就行。

还有合在一起的情况,设原来的子树有j条边,v里面有k条边。

dp[x][j+k][0]+=(dp[v][k][0]+dp[v][k][1])dp[x][j][0]dp[x][j+k][1]+=(dp[v][k][0]+dp[v][k][1]+[k0]dp[v][k1][1])dp[x][j][1]

上面那里为什么要加dp[v][k1][1]呢?因为这时xv都在点集里,可以选择(x,v)这条边。

注意合在一起的情况还要统计进答案里。

而且由于会出现贡献到自己的情况,所以要用一个辅助数组来存储。

复制代码
 1 #include<cstdio>
 2 #include<cstring>
 3 #define Rint register int
 4 using namespace std;
 5 typedef long long LL;
 6 const int N = 100003, mod = 998244353;
 7 int n, m, K, head[N], to[N << 1], nxt[N << 1], size[N];
 8 inline void add(int a, int b){
 9     static int cnt = 0;
10     to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt;
11 }
12 LL dp[N][13][2], f[13][2], ans[13], S[13][13];
13 inline void dfs(int x, int fa){
14     size[x] = 1;
15     dp[x][0][0] = 0; dp[x][0][1] = 1; ++ ans[0];
16     for(Rint i = head[x];i;i = nxt[i])
17         if(to[i] != fa){
18             dfs(to[i], x);
19             memcpy(f, dp[x], sizeof f);
20             for(Rint j = 0;j <= K && j <= size[to[i]];j ++)
21                 f[j][0] = (f[j][0] + dp[to[i]][j][0] + dp[to[i]][j][1]) % mod;
22             for(Rint j = 0;j <= K && j <= size[x];j ++)
23                 for(Rint k = 0;k <= K - j && k <= size[to[i]];k ++){
24                     LL S = (dp[to[i]][k][0] + dp[to[i]][k][1]) % mod;
25                     LL s1 = dp[x][j][0] * S % mod, s2 = dp[x][j][1] * (S + (k ? dp[to[i]][k - 1][1] : 0)) % mod;
26                     f[j + k][0] = (f[j + k][0] + s1) % mod;
27                     f[j + k][1] = (f[j + k][1] + s2) % mod;
28                     ans[j + k] = (ans[j + k] + s1 + s2) % mod;
29                 }
30             memcpy(dp[x], f, sizeof f);
31             size[x] += size[to[i]];
32         }
33 }
34 int main(){
35     scanf("%d%d%d", &n, &m, &K);
36     for(Rint i = 1;i < n;i ++){
37         int a, b;
38         scanf("%d%d", &a, &b);
39         add(a, b); add(b, a);
40     }
41     dfs(1, 0);
42     S[0][0] = 1;
43     for(Rint i = 1;i <= K;i ++)
44         for(Rint j = 1;j <= i;j ++)
45             S[i][j] = (S[i - 1][j - 1] + S[i - 1][j] * j) % mod;
46     LL fac = 1, res = 0;
47     for(Rint i = 1;i <= K;i ++){
48         fac = fac * i % mod;
49         res = (res + fac * S[K][i] % mod * ans[i] % mod) % mod;
50     }
51     printf("%lld", res);
52 }
复制代码

 

posted @   mizu164  阅读(475)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示