首页 > 其他分享 >P9669 [ICPC2022 Jinan R] DFS Order 2

P9669 [ICPC2022 Jinan R] DFS Order 2

时间:2024-04-10 20:44:33浏览次数:32  
标签:ICPC2022 遍历 sz int Jinan i64 Order dp mod

P9669 [ICPC2022 Jinan R] DFS Order 2

树形 dp+回退背包

dfs 的过程时走到 \(u\),如果走进一个子树后要回到 \(u\),那么这个子树一定全部遍历了一遍。所以方案数会跟子树遍历的方案数有关,可以预处理。设 \(h_u\) 表示 \(u\) 子树的遍历方案,假如 \(u\) 有 \(m\) 个儿子,那么有 \(h_u=m!\times \prod\limits_{v\in son_u} h_v\)。

考虑树形 dp。首先 \(u\) 点的 dfs 序与其子树无关,子树对其的影响只是 \(\times h_u\),所以根据乘法交换律,容易设 \(dp_{u,i}\) 表示 暂时不考虑 \(u\) 子树对方案数的影响,\(u\) 的 dfs 序最终为 \(i\) 的方案数。显然在这题里转移不同,为 \(dp_{u,i}\rightarrow dp_{v,j}\)。枚举 \(v\),如果要转移,需要知道此时 \(v\) 排在 \(u\) 后面 \(k\) 个位置的方案数 \(g_k\)(这里指 dfs 序)。

实际上就是在 \(u\) 的子树中选若干个排在 \(v\) 前遍历,然后遍历 \(v\)。这就是经典的 01 背包问题,我们需要知道选的子树数,还有遍历总点数,所以设 \(f_{i,j}\) 表示当前选了 \(i\) 个子树,总点数为 \(j\) 的方案数。转移易得。

然后考虑 \(f_{i,j}\) 的贡献,首先 \(i\) 个子树有 \(i!\) 种遍历顺序,除此之外还要考虑剩下的子树有 \((m-i-1)!\) 种遍历顺序,最后 \(hv\) 表示除 \(v\) 子树外的 \(\prod h_v\),\(g_{k+1}=g_{k+1}+f_{i,j}\times i!\times (m-i-1)\times hv\)。

最后 \(dp_{v,i+k}=dp_{v,i+k}+dp_{u,i}\times g_k\)。

这里的背包显然要排除当前的 \(v\),若每个儿子都跑一次,复杂度为 \(O(n^4)\),无法通过。这里背包的转移简单,可以用到回退背包,即我们可以对所有子树做一次背包,每次减去 \(v\) 对 \(f\) 的贡献即可。复杂度 \(O(n^3)\)。

回退背包转移顺序与正常转移相反,原因是循环的顺序改变是因为本身我们的 (01) 背包是要用 没更新的数 去更新数,所以现在要用 已经还原的数 去更新待还原的数。

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back
typedef long long i64;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const i64 N = 510, mod = 998244353;
int n;
i64 fac[N], inv[N];
i64 sz[N], cnt[N];
i64 g[N], h[N], f[N][N], dp[N][N]; 
std::vector<int> e[N];
i64 qpow(i64 a, i64 b) {
	i64 ret = 1;
	while(b) {
		if(b & 1) ret = ret * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ret;
}
void dfs(int u, int fa) {
	sz[u] = h[u] = 1;
	for(auto v : e[u]) {
		if(v == fa) continue;
		dfs(v, u);
		h[u] = h[u] * h[v] % mod;
		sz[u] += sz[v];
		cnt[u]++;
	}
	h[u] = h[u] * fac[cnt[u]] % mod;
}
void DP(int u, int fa) {
	memset(f, 0, sizeof(f));
	f[0][0] = 1;
	i64 m = cnt[u];
	for(auto v : e[u]) {
		if(v == fa) continue;
		for(int j = m; j >= 1; j--) {
			for(int k = sz[u]; k >= sz[v]; k--) {
				f[j][k] = (f[j][k] + f[j - 1][k - sz[v]]) % mod;
			}
		}
	}
	
	for(auto v : e[u]) {
		if(v == fa) continue;
		i64 hv = h[u] * inv[m] % mod * qpow(h[v], mod - 2) % mod;
		for(int j = 1; j <= m; j++) {
			for(int k = sz[v]; k <= sz[u]; k++) {
				f[j][k] = ((f[j][k] - f[j - 1][k - sz[v]]) % mod + mod) % mod;
			}
		} //注意循环为从小到大
		memset(g, 0, sizeof(g));
		for(int j = 0; j < m; j++) {
			for(int k = 0; k < sz[u]; k++) {
				g[k + 1] = (g[k + 1] + f[j][k] * fac[j] % mod * fac[m - j - 1] % mod * hv % mod) % mod;
			}
		}
		for(int j = 1; j <= n; j++) {
			for(int k = 1; k <= sz[u]; k++) {
				dp[v][j + k] = (dp[v][j + k] + dp[u][j] * g[k] % mod) % mod;
			}
		}
		for(int j = m; j >= 1; j--) {
			for(int k = sz[u]; k >= sz[v]; k--) {
				f[j][k] = (f[j][k] + f[j - 1][k - sz[v]]) % mod;
			}
		}
	}
	for(auto v : e[u]) {
		if(v == fa) continue;
		DP(v, u);
	}
}
void Solve() {
	std::cin >> n;         

	fac[0] = 1;
	for(int i = 1; i <= n + 1; i++) fac[i] = fac[i - 1] * i % mod;
	inv[n + 1] = qpow(fac[n + 1], mod - 2);
	for(int i = n; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
	for(int i = 1; i < n; i++) {
		int u, v;
		std::cin >> u >> v;
		e[u].pb(v), e[v].pb(u);
	}
	dfs(1, 0);
	dp[1][1] = 1;
	DP(1, 0);
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			std::cout << (dp[i][j] * h[i]) % mod << " \n"[j == n];
		}
	}
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	Solve();

	return 0;
}

标签:ICPC2022,遍历,sz,int,Jinan,i64,Order,dp,mod
From: https://www.cnblogs.com/FireRaku/p/18127371

相关文章

  • Oracle分析函数- count()/sum() over(partition by 分组 order by 排序) 详解
    优点:代码简单明了,并且执行效率高,(不影响总的记录数)如果不用这种函数去写,按照平时我们的思路首先想到的可能是子查询,那么将至少会走4次以上的全表扫描:(1)每个订单中产品数量大于3的产品至少1个(003,004)(2)每个订单中折扣标志为'1'的产品至少有2个(002,004)(3)每个订单......
  • unordered_map的理解和应用
    1、介绍unordered_map,它是一个关联容器,内部采用的是hash表结构,拥有快速检索的功能。1.1、特性关联性:通过key去检索value,而不是通过绝对地址(和顺序容器不同)无序性:使用hash表存储,内部无序Map:每个值对应一个键值键唯一性:不存在两个元素的键一样动态内存管理:使用内存管理......
  • 汇川AM400PLC一阶滞后滤波器使用介绍(FirstOrderLagFilter)
    1、一阶低通滤波器算法详细介绍PLC信号处理系列之一阶低通(RC)滤波器算法_数字rc滤波-CSDN博客文章浏览阅读4.1k次。1、先看看RC滤波的优缺点优点:采用数字滤波算法来实现动态的RC滤波,则能很好的克服模拟滤波器的缺点;1、在模拟常数要求较大的场合这种算法显得更为实用;2、对......
  • 记录一次使用unordered_set插入数据异常的问题
    问题描述问题和unordered_set有关,相关代码如下://打印unordered_set的所有值voidprintSet(conststd::unordered_set<std::string>&data){intindex=0;autoit=data.begin();for(;it!=data.end();++it){conststd::string&key=*i......
  • The 2023 ICPC Asia Jinan Regional Contest (The 2nd Universal Cup. Stage 17: Jina
    The2023ICPCAsiaJinanRegionalContest(The2ndUniversalCup.Stage17:Jinan)D.LargestDigit题意:给定两个范围la,ra,lb,rb,求在两个范围内选任意两个数相加,求最大的数位思路:暴力枚举即可,遇到9跳出循环voidsolve(){llla,ra,lb,rb;cin>>la>>r......
  • unordered_map
    \(unordered\_map\)的\(hash\)函数固定易被卡,于是采用自定义随机哈希函数\(custom\_hash\)。structcustom_hash{staticuint64_tsplitmix64(uint64_tx){x+=0x9e3779b97f4a7c15;x=(x^(x>>30))*0xbf58476d1ce4e5b9;x=(x^(x......
  • 详解DROO论文中的order-preserving quantization method(保序量化方法)
    ​一、论文概述1.原文GitHub链接DeepReinforcementLearningforOnlineComputationOffloadinginWirelessPoweredMobile-EdgeComputingNetworks2.原文大意提出了一种深度强化学习方法解决了边缘计算任务卸载决策和资源分配问题。整体分为两大部分:其中第一部......
  • ImportError: cannot import name ‘OrderedDict‘ from ‘typing‘
    问题描述使用timm时fromtimm.models.vision_transformerimportBlock遇到报错:"xxx/lib/python3.7/site-packages/torchvision/models/maxvit.py",line3,in<module>fromtypingimportAny,Callable,List,Optional,OrderedDict,Sequence,TupleImportE......
  • 红黑树(STL-ordered_map)
    目录一、概念: 二、红黑树的结点三、红黑树的插入四、迭代器(核心:结点指针)五、应用 一、概念:    为了保持AVL树的平衡性,AVL树需要频繁地调整全树整体拓扑结构,代价较大。为此在AVL树的平衡标准上进一步放宽条件,引入红黑树的概念:每个结点都是黑色或者红色......
  • Android使用MediaRecorder进行录像,暂停和继续录像的VideoUtils
    使用MediaRecorder进行录像,要注意再设置MediaRecorder的参数的时候设置,这里也是查了网上很多代码都没有一个完整能实现的,或多或少都有点问题。还有再暂停/继续录制的时候要注意将Camera的预览关闭camera.stopPreview()不然预览的界面还是会继续动给人暂停了还在录制的错觉。还有......