【ICPC2022济南站】【树形dp】【删物品背包dp】C.DFS Order 2

这篇具有很好参考价值的文章主要介绍了【ICPC2022济南站】【树形dp】【删物品背包dp】C.DFS Order 2。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【题意】

题目链接:https://codeforces.com/gym/104076/problem/C
简要题意:给定一棵n个点的有根树,对于所有的二元组 ( i , j ) (i,j) (i,j)求这颗树所有可能的dfs序中有多少个dfs序满足第 i i i个点出现在dfs序第 j j j个位置。

【思路】

赛场上假了无数次以后,我终于才理清楚了这题的dp思路。
状态定义:
定义 d p [ u ] [ i ] dp[u][i] dp[u][i]表示只考虑 u u u子树外的点的情况下,dfs序中在 u u u前面有 i i i个点的方案数。注意,这个 d p dp dp值并不能直接作为答案,还要乘上 u u u子树内部的所有可能的dfs序方案数。显然这个 d p dp dp的取值与 u u u子树的情况无关,因此这道题 d p dp dp的转移与一般树形 d p dp dp不同,这道题应当自上而下用父亲的信息更新儿子的信息。上文提到过,为了得到答案,我们还需要 u u u子树内部的dfs序方案数量,因此定义 d p 2 [ u ] dp2[u] dp2[u]表示 u u u子树内的dfs序方案数。
状态转移
设我们当前在 u u u点,我们考虑更新 u u u的一个儿子 v v v d p dp dp信息,我们需要知道dfs序有多少个点在 v v v前面,我们把这些点分为在 u u u的子树内和 u u u的子树外两类,最后类似背包的思路合并即可。

  • 对于 u u u子树外的点的信息,我们通过 u u u d p dp dp值即可获得;
  • 对于 u u u子树内的点,在 v v v前面的点的数量取决于 u u u的儿子们的排列顺序,我们可以把u的所有儿子的子树大小 s i z siz siz拿来做一次背包,与一般背包不同的是,后续用背包的结果更新dp值时还需要考虑放置顺序,因此我们还需要加一维表示当前背包的大小,便于考虑物品顺序。设 c [ i ] [ j ] c[i][j] c[i][j]表示放置了 i i i个物品,总大小为 j j j的方案数,考虑放置子树 v v v,则转移显然: c [ i ] [ j ] = c ′ [ i ] [ j ] + c ′ [ i − 1 ] [ j − s i z [ v ] ] c[i][j]=c'[i][j]+c'[i-1][j-siz[v]] c[i][j]=c[i][j]+c[i1][jsiz[v]]
    c ′ c' c表示未考虑 v v v的dp值, c c c表示已考虑 v v v的dp值,因此 i i i这一维倒序枚举更新即可不需要额外的数组。若我们要求 v v v的dp值,则我们背包里需要删除 v v v这个物品。由于背包dp的本质是一种多项式的卷积(此处的dp等价于 c ( y , x ) = ∏ v ∈ s o n u ( 1 + y ∗ x s i z [ v ] ) c(y,x)=\prod_{v\in son_u}(1+y*x^{siz[v]}) c(y,x)=vsonu(1+yxsiz[v])),放置顺序无关紧要,我们不妨假设 v v v是最后一个加入背包的物品。此时相当于我们已知c数组而需要求 c ′ c' c数组(注意,这里的 c ′ c' c数组在参考代码里就是 d d d数组)。移项可得转移:
    c ′ [ i ] [ j ] = c [ i ] [ j ] − c ′ [ i − 1 ] [ j − s i z [ v ] ] c'[i][j]=c[i][j]-c'[i-1][j-siz[v]] c[i][j]=c[i][j]c[i1][jsiz[v]]
    在得到 c ′ c' c数组后,设在 v v v前面有 j j j u u u子树内的点的方案数为 b [ j ] b[j] b[j] u u u一共有 c n t cnt cnt个儿子,则有转移:
    b [ j ] = ∑ i = 0 c n t − 1 c ′ [ i ] [ j ] ∗ i ! ∗ ( c n t − 1 − i ) ! b[j]=\sum_{i=0}^{cnt-1} c'[i][j]*i!*(cnt-1-i)! b[j]=i=0cnt1c[i][j]i!(cnt1i)!
    而对于 u u u这个点,它在dfs序中一定位于其他 u u u子树内的点的前面,我们可以在设置 c c c数组初值时考虑它,即: c [ 0 ] [ 1 ] = 1 c[0][1]=1 c[0][1]=1
  • 现在,我们已知 b b b数组和 d p dp dp数组,考虑用背包dp的思路对它们进行合并。但是我们目前仅考虑了u的儿子之间的排列顺序,尚未考虑这些儿子子树内的排序方案。设 B = ∏ x ∈ s o n u d p 2 [ x ] B=\prod_{x\in son_u}dp2[x] B=xsonudp2[x]。当我们用这些信息去更新 v v v的dp值时,需要注意除去常数B中和 v v v子树有关的信息。诚然,不除去这部分信息的话,我们直接得到的就是 v v v的答案数组,但是这并不利于我们进一步dfs求v的子树的dp信息,因此我们在这里的处理是除去这部分信息。即定义 C = ∏ x ∈ s o n u , x ≠ v = B d p 2 [ v ] C=\prod_{x\in son_u,x\neq v}=\frac B {dp2[v]} C=xsonu,x=v=dp2[v]B,有以下转移:
    对 于 i ∈ [ 1 , n ] , d p [ v ] [ i ] = C ∗ ∑ j = 0 s i z [ u ] b [ j ] ∗ d p [ u ] [ i − j ] 对于i\in [1,n],dp[v][i]=C*\sum_{j=0}^{siz[u]}b[j]*dp[u][i-j] i[1,n],dp[v][i]=Cj=0siz[u]b[j]dp[u][ij]
    j j j的上确界是小于 s i z [ u ] siz[u] siz[u]的,比赛时为了求稳所以定枚举上界为 s i z [ u ] siz[u] siz[u]

而对于dp2数组,我们需要在处理dp数组前就提前dfs一次先得到它。设 u u u总共有 c n t cnt cnt个儿子,考虑 u u u子树内所有可能的dfs序,转移显然:
d p 2 [ u ] = c n t ! ∗ ∏ d p 2 [ v ] dp2[u]=cnt!*\prod dp2[v] dp2[u]=cnt!dp2[v]
此时,第 i i i个点出现在dfs序中第 j j j个位置的方案数就是 d p [ i ] [ j − 1 ] ∗ d p 2 [ i ] dp[i][j-1]*dp2[i] dp[i][j1]dp2[i]
参考代码:(比赛时写的代码)文章来源地址https://www.toymoban.com/news/detail-638663.html

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=505,mod=998244353;
int n,dp[N][N],siz[N],c[N][N],d[N][N],b[N],fac[N],dp2[N];
vector<int>g[N];
void Add(int&x,int y){
	((x+=y)>=mod)&&(x-=mod);
}
inline int mul(int x,int y){
	return 1ll*x*y%mod;
}
inline int dec(int x,int y){
	return x<y?x-y+mod:x-y;
}
inline int ksm(int a,int b){
	int ret=1;
	while(b){
		if(b&1)ret=mul(ret,a);
		a=mul(a,a);
		b>>=1;
	}return ret;
}
void dfs1(int u,int fa){
	siz[u]=1;
	dp2[u]=1;
	int cnt=0;
	for(int v:g[u]){
		if(v==fa)continue;
		cnt++;
		dfs1(v,u);
		siz[u]+=siz[v];
		dp2[u]=mul(dp2[u],dp2[v]);
	}
	dp2[u]=mul(dp2[u],fac[cnt]);
}
void dfs2(int u,int fa){
	int cnt=0,sum=1;
	memset(c,0,sizeof c);
	c[0][1]=1;
	int lsr=1;
	for(int v:g[u]){
		if(v==fa)continue;
		cnt++;sum+=siz[v];
		lsr=mul(lsr,dp2[v]);
		for(int i=cnt;i>=1;i--)
			for(int j=sum;j>=siz[v];j--)
				Add(c[i][j],c[i-1][j-siz[v]]);
	}
	for(int v:g[u]){
		if(v==fa)continue;
		memset(d,0,sizeof d);
		for(int i=0;i<=cnt;i++)
			for(int j=0;j<=siz[u];j++)
				d[i][j]=dec(c[i][j],(j>=siz[v]&&i>0)?d[i-1][j-siz[v]]:0);
		memset(b,0,sizeof b);
		for(int i=0;i<cnt;i++)
			for(int j=0;j<=siz[u];j++)
				Add(b[j],mul(mul(fac[i],fac[cnt-i-1]),d[i][j]));
		int bas=mul(lsr,ksm(dp2[v],mod-2));
		for(int i=0;i<=n;i++)
			for(int j=0;j<=siz[u];j++)
				Add(dp[v][i+j],mul(bas,mul(dp[u][i],b[j])));
	}
	for(int v:g[u]){
		if(v!=fa)dfs2(v,u);
	}
}
int main()
{
	scanf("%d",&n);
	fac[0]=1;
	for(int i=1;i<=n;i++)fac[i]=mul(fac[i-1],i);
	for(int i=2;i<=n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dp[1][0]=1;
	dfs1(1,0);
	dfs2(1,0);
	for(int i=1;i<=n;i++){
		for(int j=0;j<n;j++)
			cout<<mul(dp[i][j],dp2[i])<<" ";
		cout<<"\n";
	}
		
}

到了这里,关于【ICPC2022济南站】【树形dp】【删物品背包dp】C.DFS Order 2的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于微信山东济南二手物品交易小程序系统设计与实现 研究背景和意义、国内外现状

     博主介绍 :黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程,免费 项目配有对应开发文档、开题报告、任务书、

    2024年02月01日
    浏览(29)
  • Unity3D实现背包系统、物品的拖拽、拾取物品功能

    要在Unity中实现背包系统,你可以创建一个脚本来管理库存和物品。 首先,在Unity中创建一个名为“InventoryManager”的C#脚本。在这个脚本中,你可以创建一个将存储在背包中的物品列表。

    2024年02月16日
    浏览(32)
  • 2022icpc西安站部分题解-E

    E. Find Maximum 题意:给定边界L和R,算满足的所有的的最大值, 其中满足: 。 题解: 打表发现发现了f(x)与x的三进制有关系,即f(x)等于x三进制的每个数相加,再加上三进制数的有效位数。下图从左向右依次是x,x的三进制,f(x)。 于是便是将问题转变为在区间中找到三进制的每

    2024年02月08日
    浏览(25)
  • 动态规划报告(树形DP+概率DP

    树形 DP,即在树上进行的 DP。由于树固有的递归性质,树形 DP 一般都是递归进行的。一般需要在遍历树的同时维护所需的信息 以一道题目为例 2022CCPC桂林站G Group Homework No, we don’t want group homework. It’s the place where 1+11 can be true. However, you are currently the leader of a group with three

    2024年02月12日
    浏览(38)
  • 动态规划之树形DP

    树形DP是指在“树”这种数据结构上进行的动态规划:给出一颗树,要求以最少的代价(或取得最大收益)完成给定的操作。通常这类问题规模比较大,枚举算法效率低,无法胜任,贪心算法不能求得最优解,因此需要用动态规划进行求解。 在树上做动态规划显得非常合适,

    2024年02月05日
    浏览(33)
  • 动态规划-树形DP

    链接:https://www.acwing.com/problem/content/848/ 给定一颗树,树中包含 n n n 个结点(编号 1 ∼ n 1 sim n 1 ∼ n )和 n − 1 n-1 n − 1 条无向边。 请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。 重心定义:重心是指树中的一个结点,如果将这个点删除后,剩

    2024年02月08日
    浏览(30)
  • 树形DP()

    Ural 大学有 N 名职员,编号为 1∼N。 他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司。 每个职员有一个快乐指数,用整数 Hi 给出,其中 1≤i≤N。 现在要召开一场周年庆宴会,不过,没有职员愿意和直接上司一起参会。 在满足这个条件的前提下,主

    2024年02月09日
    浏览(20)
  • 动态规划——树形DP 学习笔记

    前置知识:树基础。 树形 DP,即在树上进行的 DP,最常见的状态表示为 (f_{u,cdots}) ,表示以 (u) 为根的子树的某个东东。 本文将讲解一些经典题目(树的子树个数、树的最大独立集、树的最小点覆盖、树的最小支配集、树的直径、树的重心、树的中心),以及一些常见形

    2024年02月08日
    浏览(28)
  • 算法套路十九——树形DP

    树形 DP,即在树上进行的 DP。由于树固有的递归性质,这里的DP是指是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法,故虽然带有DP,但一般都是通过 递归 来进行。 给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路

    2024年02月10日
    浏览(81)
  • [ACM学习] 树形dp之换根

    总的来说: 题目描述:一棵树求哪一个节点为根时,XXX最大或最小 分为两步:1. 树形dp  2. 第二次dfs 如果暴力就是 O(n^2) , 当从1到2的时候,2及其子树所有的深度都减一,其它的点,所有的深度都加一。写成递推方程如下: 思路是:第一遍 dfs 遍历的时候先把以某一确定点

    2024年01月24日
    浏览(20)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包