题解 P8935【[JRKSJ R7] 茎】
脑子短路。
题意
你有一棵 个点的根节点为 的有根树,现在你要对这棵树进行剪枝,每次你可以选择一个还未被剪掉的节点 进行操作,然后剪掉 的子树所有点(包括 )。当且仅当你剪掉 时,操作停止。
你需要在第 次剪枝时对 进行操作,而非仅仅将其剪掉,即你不能在第 次对其祖先进行操作使其被连带剪掉。
求有多少种不同的操作序列,两个操作序列不同当且仅当长度不同或存在一次操作 使得两操作序列在第 次时选择的 不同。输出答案模 。
。
思路
考虑先算 表示 子树内进行 次操作,不必须删完的方案数。先得到不考虑 的方案数,转移为 ,。再考虑 ,。
令 与其祖先组成的点集为 ,从 开始向下倒序删点。我们定义 表示考虑到 的前 位,我们已经钦定了 在 前的点的不在 中的子树的 个操作。这些操作有相对顺序,但还未被放在操作序列中。
我们令 表示 不在 内的子树进行 次操作的方案数。,。
考虑一次转移,当 不被操作时,。注意最后一个点不能不被操作,所以不能复制。当 被放在 位置时, 都可以转移到 。再将 不在 中的子树的操作加入到 中。。
最终的答案为 。
这时的时间复杂度为 ,考虑优化。我们令 表示 。则上述转移中的 可以直接由 转移得到,此时时间复杂度优化到 ,即可通过。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=500+10,mod=1e9+7;
int n,k,p,fac[N],ifac[N],cnt,head[N];
struct Edge{
int to,nxt;
}a[N<<1];
inline void add(int u,int v){
cnt++;
a[cnt].to=v;
a[cnt].nxt=head[u];
head[u]=cnt;
}
inline int qpow(int x,int y){
int res=1;
while(y){
if(y&1) res=1ll*res*x%mod;
x=1ll*x*x%mod;
y/=2;
}
return res;
}
inline void Prefix(int n){
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=1ll*fac[i-1]*i%mod;
ifac[n]=qpow(fac[n],mod-2);
for(int i=n;i;i--)
ifac[i-1]=1ll*ifac[i]*i%mod;
}
inline int C(int n,int m){
return n<m?0:1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int f[N][N],g[2][N*2],pr[N],tmp[N],fr[N],siz[N],ans;
bool fl[N];
//g[i][j][k] i 及 i 祖先的其它子树内已经选了 k 个
inline void dfs(int x,int fa){
f[x][0]=1,fr[x]=fa;
for(int i=head[x];i;i=a[i].nxt){
int t=a[i].to;
if(t==fa) continue;
dfs(t,x);
for(int j=siz[x];j>=0;j--)
for(int k=siz[t];k>=1;k--)
f[x][j+k]=(f[x][j+k]+1ll*f[x][j]*f[t][k]%mod*C(j+k,k))%mod;
siz[x]+=siz[t];
}
for(int i=siz[x];i>=0;i--)
f[x][i+1]=(f[x][i+1]+f[x][i])%mod;
siz[x]++;
}
inline void oth(int x){//这个节点其它子树 i 次操作的方案数
int s=0;
tmp[0]=1;
for(int i=1;i<=n;i++)
tmp[i]=0;
for(int i=head[x];i;i=a[i].nxt){
int t=a[i].to;
if(t==fr[x]||fl[t]) continue;
for(int j=s;j>=0;j--)
for(int k=siz[t];k>=1;k--)
tmp[j+k]=(tmp[j+k]+1ll*tmp[j]*f[t][k]%mod*C(j+k,k))%mod;
s+=siz[t];
}
}
vector<int>s;
int main(){
n=read(),k=read(),p=read();
Prefix(n);
for(int i=1;i<n;i++){
int u=read(),v=read();
add(u,v),add(v,u);
}
dfs(1,0);
int t=p;
while(t){
s.push_back(t);
fl[t]=1,t=fr[t];
}
reverse(s.begin(),s.end());
oth(s[0]);
int l=s.size();
for(int j=0;j<=n;j++)
g[0][j]=tmp[j];
for(int i=1;i<s.size();i++){
int c=i&1;
oth(s[i]);
int sz=siz[s[i]];
if(i!=l-1) sz-=siz[s[i+1]];
for(int t=0;t<=n;t++)
g[c][t]=g[!c][t]*(i!=l-1);
for(int t=n;t>=0;t--)
pr[t]=(pr[t+1]+g[!c][t])%mod;
for(int d=n;d>=0;d--)
g[c][d]=(g[c][d]+pr[d])%mod;
for(int j=n-1;j>=0;j--)
for(int d=sz;d;d--)
g[c][j+d]=(g[c][j+d]+1ll*g[c][j]*tmp[d]%mod*C(j+d,d))%mod;
}
write(g[l-1&1][k-1]);
flush();
}
再见 qwq~
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】