CF1111E - Tree
2021/5/10 18:25:10
本文主要是介绍CF1111E - Tree,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
CF1111E - Tree
题目大意
给定一棵无根树\(T\),\(q\)次查询每次查询一个给定一个根\(r\),点集\(S\)和限制\(m\)
求将\(S\)分成不超过\(m\)个非空集合,使得最终每个集合内不存在两点为祖先关系
分析
容易发现题目是一个给定部分点集的树形\(dp\),因此需要用虚树来处理
将\(r\)也加入虚树,从\(r\)开始\(\text{dfs}\)即确定了根为\(r\)
dp部分
一种思路是树形背包,计算子树内分为\(i\)个集合的方案数,枚举在\(\text{LCA}\)处合并两个集合
但是由于要枚举合并的个数,难以写出优秀的复杂度
由于一条祖先链上点之间的集合独立,容易描述,因此可以考虑\(\text{dfs}\)序dp
按照\(\text{dfs}\)依次加入每一个点\(u\),令\(dp_i\)表示当前有\(i\)个集合的方案数
则在\(i\)个集合中包含\(dep_u\)个集合\(u\)无法加入
枚举\(i\),加入一个点\(O(m)\)转移,滚动数组即可
复杂度为\(O(n\log n+\sum |S|\cdot m)\)
const int N=1e5+10,P=1e9+7; int n,m; int L[N],top[N],son[N],sz[N],dfn,fa[N],dep[N]; struct Edge{ int to,nxt; } e[N<<1]; int head[N],ecnt; void AddEdge(int u,int v){ e[++ecnt]=(Edge){v,head[u]}; head[u]=ecnt; } void dfs(int u){ sz[u]=1; for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to; if(v==fa[u]) continue; dep[v]=dep[fa[v]=u]+1,dfs(v); if(sz[v]>sz[son[u]]) son[u]=v; sz[u]+=sz[v]; } } void dfs(int u,int t){ top[u]=t,L[u]=++dfn; if(son[u]) dfs(son[u],t); for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to; if(v==son[u] || v==fa[u]) continue; dfs(v,v); } } int LCA(int x,int y){ while(top[x]!=top[y]) dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]]; return dep[x]<dep[y]?x:y; } vector <int> G[N]; int stk[N],T; void Link(int u,int v){ G[u].pb(v),G[v].pb(u); } void Ins(int u){ if(T<=1) return void(stk[++T]=u); int lca=LCA(u,stk[T]); if(lca==stk[T]) return void(stk[++T]=u); while(T>1 && L[stk[T-1]]>=L[lca]) Link(stk[T],stk[T-1]),T--; if(stk[T]!=lca) Link(stk[T],lca),stk[T]=lca; stk[++T]=u; } int dis[N],mk[N]; int dp[310],a[N],c,rt; void dfs_dp(int u,int f){ dis[u]=dis[f]+mk[u]; if(mk[u]) drep(i,m,0) dp[i]=((i?dp[i-1]:0)+1ll*(i-dis[f])*dp[i])%P; for(int v:G[u]) if(v!=f) dfs_dp(v,u); mk[u]=0,G[u].clear(); } int main(){ n=rd(),m=rd(); rep(i,2,n){ int u=rd(),v=rd(); AddEdge(u,v),AddEdge(v,u); } dfs(1),dfs(1,1); rep(_,1,m) { c=rd(),m=rd(),rt=rd(); rep(i,1,c) mk[a[i]=rd()]=1; a[++c]=1,a[++c]=rt; sort(a+1,a+c+1,[&](int x,int y){ return L[x]<L[y]; }); T=0; rep(i,1,c) if(a[i]!=a[i-1]) Ins(a[i]); while(T>1) Link(stk[T-1],stk[T]),T--; rep(i,0,m) dp[i]=0; dp[0]=1; dfs_dp(rt,0); int ans=0; rep(i,1,m) ans+=dp[i],Mod1(ans); printf("%d\n",ans); } }
这篇关于CF1111E - Tree的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享