题面传送门

题意:

给出一张无向图,每次你可以选择一个度数 \(\leq 1\) 的点并将其删除。

问对于 \(k=0,1,2,\dots,n\),有多少个删除 \(k\) 个点的序列,答案模 \(10^9+9\)。

\(1\leq n\leq 100\)

很容易发现,环上的点是永远不可能被删除的。于是我们对原图做一遍拓扑排序,求出哪些点可能被删除。

显然这些能被删除的点一定会形成一棵森林。

而森林中的每个连通块又可以分为两类:一种是该连通块在原图中属于的连通块中有环相连。另一种是该连通块在原图中本身就是一棵树。

对于第一种情况,一定有且仅有一个点 \(x\) 与环相邻。此时 \(x\) 一定是连通块中最后一个被删除的。我们就以 \(x\) 为根跑一遍树上背包。具体来说,设 \(dp_{i,j}\) 表示 \(i\) 子树中选了 \(j\) 个点的方案数。由于我们这是有根树,必须选完 \(x\) 子树内所有点才能选 \(x\)。转移就正常求个卷积,再乘上个组合数的系数即可。由于任意两点之间最多被合并一次,复杂度 \(n^2\)。

对于第二种情况,我们并没有给这棵树定根。所以我们考虑以树上每个点为根都跑一次树上背包。但这样会重复计算。对于选了 \(i\) 个点的方案,在以未被选择 \(s-i\) 个点为根情况下都被算了一次。故答案除以 \(s-i\)。

然后最后再总体跑一遍背包即可。时间复杂度 \(\mathcal O(n^3)\)

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=1;
while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXN=100;
const int MAXM=4950;
const int MOD=1e9+9;
int qpow(int x,int e){int ret=1;for(;e;x=1ll*x*x%MOD,e>>=1) if(e&1) ret=1ll*ret*x%MOD;return ret;}
int n,m,deg[MAXN+5],to[MAXM*2+5],ec=0,nxt[MAXM*2+5],hd[MAXN+5];
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
bool vis[MAXN+5];
int from[MAXN+5],cmp=0,siz[MAXN+5],rt[MAXN+5],con[MAXN+5],c[MAXN+5][MAXN+5];
void dfs0(int x){
if(from[x]) return;from[x]=cmp;siz[cmp]++;
// printf("%d %d\n",x,cmp);
for(int e=hd[x];e;e=nxt[e]) if(vis[to[e]]) dfs0(to[e]);
}
int dp[MAXN+5][MAXN+5],s[MAXN+5],tmp[MAXN+5],dpc[MAXN+5][MAXN+5];
void dfs(int x,int f){
// printf("%d %d\n",x,f);
s[x]=1;dp[x][0]=1;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;dfs(y,x);
memset(tmp,0,sizeof(tmp));
for(int i=0;i<=s[x];i++) for(int j=0;j<=s[y];j++)
tmp[i+j]=(tmp[i+j]+1ll*dp[x][i]*dp[y][j]%MOD*c[i+j][i]%MOD)%MOD;
s[x]+=s[y];
for(int i=0;i<=s[x];i++) dp[x][i]=tmp[i];
} dp[x][s[x]]=dp[x][s[x]-1];
// for(int i=0;i<=s[x];i++) printf("%d %d %d\n",x,i,dp[x][i]);
}
int iv[MAXN+5],ans[MAXN+5];
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<=MAXN;i++){
c[i][0]=1;
for(int j=1;j<=i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
}
iv[0]=1;for(int i=1;i<=MAXN;i++) iv[i]=qpow(i,MOD-2);
for(int i=1;i<=m;i++){
int u,v;scanf("%d%d",&u,&v);
adde(u,v);adde(v,u);deg[u]++;deg[v]++;
}
queue<int> q;
for(int i=1;i<=n;i++) if(deg[i]<=1) q.push(i);
while(!q.empty()){
int x=q.front();q.pop();vis[x]=1;//printf("%d\n",x);
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];deg[y]--;
if(deg[y]<=1&&!vis[y]) q.push(y);
}
}
for(int i=1;i<=n;i++) if(vis[i]&&!from[i]) cmp++,dfs0(i);
for(int i=1;i<=n;i++) for(int e=hd[i];e;e=nxt[e]) if(vis[i]&&!vis[to[e]]) con[i]=to[e],rt[from[i]]=i;
// for(int i=1;i<=cmp;i++) printf("%d\n",rt[i]);
for(int i=1;i<=n;i++) if(vis[i]){
if(rt[from[i]]&&i==rt[from[i]]){
memset(dp,0,sizeof(dp));memset(s,0,sizeof(s));
dfs(i,con[i]);
for(int j=0;j<=siz[from[i]];j++) dpc[from[i]][j]=dp[i][j];
} else if(!rt[from[i]]){
memset(dp,0,sizeof(dp));memset(s,0,sizeof(s));
dfs(i,0);//printf("%d\n",i);
// for(int j=1;j<=siz[from[i]];j++) printf("%d ",dp[i][j]);printf("\n");
for(int j=0;j<=siz[from[i]];j++) dpc[from[i]][j]=(dpc[from[i]][j]+dp[i][j])%MOD;
}
}
for(int i=1;i<=cmp;i++) if(!rt[i]){
for(int j=0;j<=siz[i];j++) dpc[i][j]=1ll*dpc[i][j]*iv[siz[i]-j]%MOD;
} ans[0]=1;
// for(int i=1;i<=cmp;i++) for(int j=0;j<=siz[i];j++) printf("%d %d %d\n",i,j,dpc[i][j]);
for(int i=1;i<=cmp;i++) for(int j=n;j;j--) for(int k=1;k<=min(siz[i],j);k++)
ans[j]=(ans[j]+1ll*ans[j-k]*dpc[i][k]%MOD*c[j][k])%MOD;
for(int i=0;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}

Codeforces 512D - Fox And Travelling(树上背包)的更多相关文章

  1. cf 512D - Fox And Travelling

    题目大意 给定一颗\(n\le 100\)个点的图,可以进行随机游走,求游走\(k=0...n\)个点的方案数 游走的规则是:每次只能访问一个度数\(\le 1\)的点,并将其删除 分析 看完傻眼 问 ...

  2. Codeforces 1097G - Vladislav and a Great Legend(第二类斯特林数+树上背包)

    Codeforces 题目传送门 & 洛谷题目传送门 首先看到这题我的第一反应是:这题跟这题长得好像,不管三七二十一先把 \(k\) 次方展开成斯特林数的形式,\(f(X)^k=\sum\li ...

  3. HDU4044 GeoDefense(有点不一样的树上背包)

    题目大概说一棵n个结点的树,每个结点都可以安装某一规格的一个塔,塔有价格和能量两个属性.现在一个敌人从1点出发但不知道他会怎么走,如果他经过一个结点的塔那他就会被塔攻击失去塔能量的HP,如果HP小于等 ...

  4. luogu 2014 选课 树上背包

    树上背包 #include<bits/stdc++.h> using namespace std; ; const int inf=0x3f3f3f3f; vector<int> ...

  5. BZOJ 4753 [Jsoi2016]最佳团体 | 树上背包 分数规划

    BZOJ 4753 [Jsoi2016]最佳团体 | 树上背包 分数规划 又是一道卡精度卡得我头皮发麻的题-- 题面(--蜜汁改编版) YL大哥是24OI的大哥,有一天,他想要从\(N\)个候选人中选 ...

  6. 洛谷 P2015 二叉苹果树 (树上背包)

    洛谷 P2015 二叉苹果树 (树上背包) 一道树形DP,本来因为是二叉,其实不需要用树上背包来干(其实即使是多叉也可以多叉转二叉),但是最近都刷树上背包的题,所以用了树上背包. 首先,定义状态\(d ...

  7. 【BZOJ】4033: [HAOI2015]树上染色 树上背包

    [题目]#2124. 「HAOI2015」树上染色 [题意]给定n个点的带边权树,要求将k个点染成黑色,使得 [ 黑点的两两距离和+白点的两两距离和 ] 最大.n<=2000. [算法]树上背包 ...

  8. 【BZOJ】4753: [Jsoi2016]最佳团体 01分数规划+树上背包

    [题意]n个人,每个人有价值ai和代价bi和一个依赖对象ri<i,选择 i 时 ri 也必须选择(ri=0时不依赖),求选择k个人使得Σai/Σbi最大.n<=2500,ai,bi< ...

  9. bzoj 4753: [Jsoi2016]最佳团体【01分数规划+二分+树上背包】

    01分数规划,二分答案然后把判别式变成Σp[i]-Σs[i]*mid>=0,然后树上背包判断,设f[i][j]为在i点子树里选j个的最大收益,随便背包一下就好 最丧病的是神卡常--转移的时候要另 ...

随机推荐

  1. javascript-vue介绍

    vue.js是一个用于创建web交互页面的库 从技术角度讲,vue专注于MVVM模型的viewModel层,它通过双向数据绑定把view层和model层连接起来,实际DOM封装和输出格式都被抽象为Di ...

  2. [对对子队]Beta设计和计划

    需求再分析 Alpha阶段用户反馈的问题主要有三个 新手引导部分没有明确指出合成按钮可以使用下拉框切换目标,因此不少玩家卡在第三关 觉得合成动画太长,希望可以快进或者跳过 对游戏目标很迷惑,不知道为什 ...

  3. [对对子队]测试报告Beta

    一.测试中发现的bug BETA阶段的新bug 描述 提出者(可能需要发现者在会议上复现) 处理人 是否解决 第四关中工作区的循环语句拖动到组件区后成本的大小比原来不一样的问题 梁河览 何瑞 是 循环 ...

  4. Scrum Meeting 1补充会议

    日期:2021年04月24日 会议主要内容概述: 本次会议于11:30举行,对项目架构做出了重要调整,并根据该调整修改了第1次例会报告中后两日计划完成的工作部分. 一.架构调整 会上讨论了用户模块相关 ...

  5. Codeforces Round #573 (Div. 2) D题题解

    一.题目 ​ Tokitsukaze, CSL and Stone Game ​ Tokitsukaze和CSL正在玩一些石头游戏. ​ 一开始,有n堆的石头,第i堆石头数记为 \(a_i\),两人轮 ...

  6. 局域网(以太网与IEEE 802.3、IEEE 802.11、)

    文章转自:https://blog.csdn.net/weixin_43914604/article/details/105016637 学习课程:<2019王道考研计算机网络> 学习目的 ...

  7. 表示数值的字符串 牛客网 剑指Offer

    表示数值的字符串 牛客网 剑指Offer 题目描述 请实现一个函数用来判断字符串是否表示数值(包括整数和小数).例如,字符串"+100","5e2"," ...

  8. Luogu P2467 [SDOI2010]地精部落 | 神奇的dp

    题目链接 DP 题目大意:给定一个数n,求1~n这n个整数的所有排列中有多少个波动数列,将这个数量%p后输出. 什么是波动数列呢?顾名思义,就是一个大.一个小.一个大.一个小--或者是一个小.一个大. ...

  9. linux 内核源代码情景分析——linux 内存管理的基本框架

    386 CPU中的页式存管的基本思路是:通过页面目录和页面表分两个层次实现从线性地址到物理地址的映射.这种映射模式在大多数情况下可以节省页面表所占用的空间.因为大多数进程不会用到整个虚存空间,在虚存空 ...

  10. Oracle创建表、删除表、修改表、字段增删改 语句总结

    创建表: create table 表名 ( 字段名1 字段类型 默认值 是否为空 , 字段名2 字段类型 默认值 是否为空, 字段名3 字段类型 默认值 是否为空, ...... ); 创建一个us ...