Codeforces 1097G Vladislav and a Great Legend [树形DP,斯特林数]
这题真是妙的很。
通过看题解,终于知道了\(\sum_n f(n)^k\)这种东西怎么算。
update:经过思考,我对这题有了更深的理解,现将更新内容放在原题解下方。
思路
发现\(\sum_S (f(S))^k\)这东西因为有个\(k\)次方,所以特别难算,考虑拆开:
\]
其中\(S(n,m)\)是第二类斯特林数,即\(n\)个元素放进\(m\)个集合里的方案数。(不会打那个大括号,会的请在评论区教我一下,谢谢)
于是式子化为:
ans&=\sum_S (f(S))^k\\
&=\sum_S \sum_{i=0}^k {f(S) \choose i} i!S(k,i)\\
&=\sum_{i=0}^k i!S(k,i)\sum_S {f(S) \choose i}
\end{align*}
\]
外面可以枚举\(i\),考虑里面那一堆东西怎么搞。
考虑它的组合意义:对于每一个点集组成的生成树,在树里面选出\(i\)条边涂色的方案数。
听着像是个背包,实际就是背包。
设\(dp_{x,i}\)表示\(x\)的子树内选出一棵有\(i\)条涂了色的边的生成树的方案数,答案在每一棵生成树中深度最小的点上统计,则有
\]
也就是个背包。
但实际的DP比这恶心很多,有很多细节。
复杂度看似是\(O(nk^2)\),但据说是\(O(nk)\),不过我不会证。
感觉对这题的理解还不够透彻,但网上也没什么题解,就这样吧。
代码
#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define drep(i,x,y) for (int i=(x);i>=(y);i--)
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define sz 101010
typedef long long ll;
typedef double db;
const ll mod=1e9+7;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
template<typename T>inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
template<typename T>inline void read(T& t)
{
t=0;char f=0,ch=getchar();double d=0.1;
while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
t=(f?-t:t);
}
template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
void file()
{
#ifndef ONLINE_JUDGE
freopen("a.txt","r",stdin);
#endif
}
// inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;
int n,K;
struct hh{int t,nxt;}edge[sz<<1];
int head[sz],ecnt;
void make_edge(int f,int t)
{
edge[++ecnt]=(hh){t,head[f]};
head[f]=ecnt;
edge[++ecnt]=(hh){f,head[t]};
head[t]=ecnt;
}
inline ll add(ll x,ll y){x+=y;if (x>=mod) x-=mod;return x;}
ll dp[sz][233],f[sz],ans[233];
int size[sz];
#define v edge[i].t
void dfs(int x,int fa)
{
size[x]=1;dp[x][0]=2; // 选了自己和没选自己都合法
go(x) if (v!=fa)
{
dfs(v,x);
rep(i,0,K) f[i]=0;
rep(j,0,min(K,size[x])) // 一定要取min!!
rep(k,0,min(K-j,size[v]))
f[j+k]=add(f[j+k],dp[x][j]*dp[v][k]%mod);
rep(j,0,K) dp[x][j]=f[j];
rep(j,0,K) ans[j]=add(ans[j],mod-dp[v][j]); // 减去只在一棵子树有的,即不经过x的
size[x]+=size[v];
}
rep(i,0,K) ans[i]=add(ans[i],dp[x][i]);
drep(i,K,1) dp[x][i]=add(dp[x][i],dp[x][i-1]); // 考虑上通往祖先的那条边
dp[x][1]=add(dp[x][1],mod-1); // 减去子树为空但连了祖先边的一种情况
}
#undef v
void solve(){dfs(1,0);}
ll S[233][233];
int main()
{
file();
int x,y;
read(n,K);
rep(i,1,n-1) read(x,y),make_edge(x,y);
S[1][1]=1;
rep(i,2,K)
rep(j,1,K)
S[i][j]=add(S[i-1][j-1],S[i-1][j]*j%mod);
int fac=1,Ans=0;
solve();
rep(i,1,K)
{
fac=1ll*fac*i%mod;
Ans=add(Ans,1ll*fac*S[K][i]%mod*ans[i]%mod);
}
cout<<Ans;
return 0;
}
更新
原来的那种DP方法有很多特殊情况需要考虑,感觉不是很好,这里给出另外一种。
设\(dp_{x,i}\)表示\(x\)的子树内选出一棵有\(i\)条涂了色的边的非空生成树的方案数,方程也差不多,但多了一些分类讨论。
考虑原来的\(x\)加入了新的子树\(v\),那么:
- 只有原来的生成树
- 只有新的子树(加上\(x\rightarrow v\)这条边)
- 由原来的和新的共同组成(并加上\(x\rightarrow v\)这条边)
加上分类讨论和非空的限制之后,思路清晰了很多(至少我是这么认为的),也避免了很多特殊情况。
至于复杂度的问题,似乎取个min之后变成\(n^2\)(\(nk\))是很多树形DP的trick,看来是我孤陋寡闻了。
#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define drep(i,x,y) for (int i=(x);i>=(y);i--)
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define sz 101010
typedef long long ll;
typedef double db;
const ll mod=1e9+7;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
template<typename T>inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
template<typename T>inline void read(T& t)
{
t=0;char f=0,ch=getchar();double d=0.1;
while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
t=(f?-t:t);
}
template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
void file()
{
#ifndef ONLINE_JUDGE
freopen("a.txt","r",stdin);
#endif
}
// inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;
int n,K;
struct hh{int t,nxt;}edge[sz<<1];
int head[sz],ecnt;
void make_edge(int f,int t)
{
edge[++ecnt]=(hh){t,head[f]};
head[f]=ecnt;
edge[++ecnt]=(hh){f,head[t]};
head[t]=ecnt;
}
inline ll add(ll x,ll y){x+=y;if (x>=mod) x-=mod;return x;}
ll dp[sz][233],f[sz],ans[233];
int size[sz];
#define v edge[i].t
void dfs(int x,int fa)
{
size[x]=1;dp[x][0]=1;
go(x) if (v!=fa)
{
dfs(v,x);
rep(j,0,K) f[j]=dp[x][j];
rep(j,0,K) dp[x][j]=dp[x][j]; // 只有原来的
rep(j,1,min(K,size[v])) // 只有新的
dp[x][j]=add(dp[x][j],add(dp[v][j],dp[v][j-1]));
dp[x][0]=add(dp[x][0],dp[v][0]); // 只有新的
rep(j,0,min(K,size[x])) // 原来的和新的合并,顺便更新答案
rep(k,0,min(K-j,size[v]))
{
ll val=f[j]*dp[v][k]%mod;
dp[x][j+k]=add(dp[x][j+k],val);
ans[j+k]=add(ans[j+k],val);
dp[x][j+k+1]=add(dp[x][j+k+1],val);
ans[j+k+1]=add(ans[j+k+1],val);
}
size[x]+=size[v];
}
}
#undef v
void solve(){dfs(1,0);}
ll S[233][233];
int main()
{
file();
int x,y;
read(n,K);
rep(i,1,n-1) read(x,y),make_edge(x,y);
S[1][1]=1;
rep(i,2,K)
rep(j,1,K)
S[i][j]=add(S[i-1][j-1],S[i-1][j]*j%mod);
int fac=1,Ans=0;
solve();
rep(i,1,K)
{
fac=1ll*fac*i%mod;
Ans=add(Ans,1ll*fac*S[K][i]%mod*ans[i]%mod);
}
cout<<Ans;
return 0;
}
Codeforces 1097G Vladislav and a Great Legend [树形DP,斯特林数]的更多相关文章
- Codeforces 1097G - Vladislav and a Great Legend(第二类斯特林数+树上背包)
Codeforces 题目传送门 & 洛谷题目传送门 首先看到这题我的第一反应是:这题跟这题长得好像,不管三七二十一先把 \(k\) 次方展开成斯特林数的形式,\(f(X)^k=\sum\li ...
- CodeForces 1097G. Vladislav and a Great Legend
题目简述:给定$n \leq 10^5$个节点的树$T = (V, E)$,令$X \subseteq V$表示一个非空节点集合,定义$f(X)$为包含$X$的最小子树的边数.求 $$ \sum_{\ ...
- BZOJ2159 Crash的文明世界(树形dp+斯特林数)
根据组合意义,有nk=ΣC(n,i)*i!*S(k,i) (i=0~k),即将k个有标号球放进n个有标号盒子的方案数=在n个盒子中选i个将k个有标号球放入并且每个盒子至少有一个球. 回到本题,可以令f ...
- codeforces 816 E. Karen and Supermarket(树形dp)
题目链接:http://codeforces.com/contest/816/problem/E 题意:有n件商品,每件有价格ci,优惠券di,对于i>=2,使用di的条件为:xi的优惠券需要被 ...
- codeforces 161 D. Distance in Tree(树形dp)
题目链接:http://codeforces.com/problemset/problem/161/D 题意:给出一个树,问树上点到点的距离为k的一共有几个. 一道简单的树形dp,算是一个基础题. 设 ...
- Codeforces 1606F - Tree Queries(虚树+树形 dp)
Codeforces 题面传送门 & 洛谷题面传送门 显然我们选择删除的点连同 \(u\) 会形成一个连通块,否则我们如果选择不删除不与 \(u\) 在同一连通块中的点,答案一定更优. 注意到 ...
- Codeforces 219D Choosing Capital for Treeland(树形DP)
题目是给一张边有向的树形图.要选出首都的点,首都要都能走到其他点,因此要反转一些边的方向.问可以选哪几个点作为首都,使它们所需反转边的数量最少. 这题挺好想的,因为做过HDU2196. 首先就不妨设正 ...
- Codeforces 980F Cactus to Tree 仙人掌 Tarjan 树形dp 单调队列
原文链接https://www.cnblogs.com/zhouzhendong/p/CF980F.html 题目传送门 - CF980F 题意 给定一个 $n$ 个节点 $m$ 条长为 $1$ 的边 ...
- Codeforces 835 F Roads in the Kingdom(树形dp)
F. Roads in the Kingdom(树形dp) 题意: 给一张n个点n条边的无向带权图 定义不便利度为所有点对最短距离中的最大值 求出删一条边之后,保证图还连通时不便利度的最小值 $n & ...
随机推荐
- IT这条路,适合什么人走。
今天 ,到图书馆Study,呼,不知道为撒,看到那么多新书,那么多新技术(也不能说是新技术,就是自己没有学习过的技术),特别兴奋,学习的疲劳顿时间就没了,感觉什么都想学,都想据为己有,但是...... ...
- 为什么要用日志框架 Logback 基本使用
[日志框架]以时间为单位描述应用项目运行状态:用户下线.接口超时.数据库崩溃等等一系列事件 [日志框架能力] 1.定制输出格式 2.定制输出目标 3.携带 Context 比如 HelloWorld. ...
- HTTP 返回状态码
一.HTTP 超文本传输协议 HTTP 是基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议. 一个HTTP "客户端"是一个应用程 ...
- 在PHP中使用CURL,“撩”服务器只需几行
在PHP中使用CURL,“撩”服务器只需几行https://segmentfault.com/a/1190000006220620 七夕啦,作为开发,妹子没得撩就“撩”下服务器吧,妹子有得撩的同学那就 ...
- Food Log with Speech Recognition and NLP
1. 分词 word segmentation 国内有jieba 分词 2. Named Entity Recognition 训练自己的Model How can I train my own NE ...
- Coursera, Machine Learning, Neural Networks: Representation - week4/5
Neural Network Motivations 想要拟合一条曲线,在feature 很多的情况下,feature的组合也很多,在现实中不适用,比如在computer vision问题中featu ...
- sublime text 3 3103 注册码
—– BEGIN LICENSE —– Ryan Clark Single User License EA7E-812479 2158A7DE B690A7A3 8EC04710 006A5EEB 3 ...
- Kaldi的BaseLine训练过程
steps/train_mono.sh --nj "$train_nj" --cmd "$train_cmd" data/train data/lang exp ...
- modbus 寄存器介绍
modbus 的查询命令 命令 地址开始(两个地址) 地址长度(两个地址) 检验 01 xx xx xx ...
- mysql 案例 ~ mysql字符集详解
一 谈谈mysql常见的字符集问题 二 字符集统一 1 character_set_server 2 character_set_client 3 java/php等连接字符集 4 chara ...