[LOJ#3044][动态DP]「ZJOI2019」Minimax 搜索
- 容易想到一种暴力 DP:先转化成对于每个 \(k\) 求出 \(\max_{i\in S}|i-w_i|\le k\) 的方案数,最后差分 
- 然后问题转化成每个叶子的权值有个取值区间,注意这时我们可以把每个点的权值分成 \(<W,=W,>W\)(看作 \(0,1,2\) )三类处理,然后 DP \(f[u][0/1/2]\) 
- 然后你会很快发现这么做不行,因为选取某些叶子集合时,根节点的权值可以小于 \(W\) 也可以大于 \(W\) ,直接用 \(2^m-1\) 减掉 \(f[u][0]+f[u][2]\) 无法得到正确结果 
- 于是我们尝试考虑怎样才能在 \(<W\) 和 \(>W\) 这两个条件中去掉一个 
- 考虑权值为 \(W\) 的叶子到根的一条链 
- 容易发现根节点的权值会被改变,当且仅当这条链上存在一个点会被改变 
- 设这条链上有一个深度为奇数的点 \(u\) (偶数同理),考虑把链上所有的边都删掉之后 \(u\) 所在的连通块 
- 易得在原树上 \(u\) 的权值会被改,当且仅当删边之后就 \(u\) 所在的连通块,能让 \(u\) 的权值大于 \(W\) 
- 特殊地,如果 \(u\) 是叶子那么 \(u\) 的权值会被改当且仅当 \(u\) 在选定集合内且 \(k>0\) 
- 这样我们就实现了在 \(<W\) 和 \(>W\) 这两个条件中去掉一个 
- 我们有了一个 DP:\(f[u]\) 表示 \(u\) 的子树内的叶子节点有多少个子集能让 \(u\) 的权值大于 \(W\) 
- 如果 \(u\) 的深度为奇数(\(cnt_u\) 为 \(u\) 的子树内叶子个数): 
- \[f[u]=2^{cnt_u}-\prod_{v\in son[u]}(2^{cnt_v}-f[v])
 \]
- 否则: 
- \[f[u]=\prod_{v\in son[u]}f[v]
 \]
- 把所有连通块的 DP 结果用 \(2^{cnt}\) 减掉后乘起来,即为根节点权值不会被改变的方案数 
- 对每个 \(k\) 进行 DP 的复杂度为 \(O(n^2)\) 
- 考虑如何优化。我们注意到这个 DP 的转移和 \(k\) 无关,只有初始值(叶子)和 \(k\) 有关 
- 而如果一个点 \(u\) 所在的连通块根在原树中的深度为奇数,那么点 \(u\) 的初值应该为: 
- \[f[u]=[u>W]+[u+k>W]
 \]
- 注意到当 \(k\) 不断加一的时候, \([u+k>W]\) 只会改变一次 
- 于是从小到大枚举 \(k\) 后动态 DP 即可 
- 注意由于 \(f[u]\) 可以为 \(0\) ,所以往上更新 DP 值时不能把 \(f[fa[u]]\) 直接除以 \(f[u]\) ,而需要对每个点维护一个变量表示子节点的 DP 值中有多少个 \(0\) ,另一个变量表示子节点不为 \(0\) 的 DP 值之积 
- \(O(n\log^2n)\) 
Code
#include <bits/stdc++.h>
#define p2 p << 1
#define p3 p << 1 | 1
template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}
template <class T>
inline T Min(const T &a, const T &b) {return a < b ? a : b;}
template <class T>
inline T Max(const T &a, const T &b) {return a > b ? a : b;}
const int N = 2e5 + 5, M = N << 1, L = M << 1, rqy = 998244353;
int n, l, r, dep[N], ecnt, nxt[M], adj[N], go[M], val[N], ans[N], f[N], cnt[N],
fa[N], sze[N], son[N], top[N], pos[N], idx[N], bot[N], QAQ, pw[N], re[N], c0[N],
rea = 1, cnt0;
bool lea[N], bel[N];
int qpow(int a, int b)
{
	int res = 1;
	while (b)
	{
		if (b & 1) res = 1ll * res * a % rqy;
		a = 1ll * a * a % rqy;
		b >>= 1;
	}
	return res;
}
struct modi
{
	int a, b;
	friend inline modi operator * (modi x, modi y)
	{
		return (modi) {(int) (1ll * x.a * y.a % rqy),
			(int) ((1ll * x.a * y.b + x.b) % rqy)};
	}
	friend inline int operator * (modi x, int y)
	{
		return (1ll * x.a * y + x.b) % rqy;
	}
} g[N], gp[L];
void add_edge(int u, int v)
{
	nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
	nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}
void dfs(int u, int fu)
{
	dep[u] = dep[fu] + 1;
	val[u] = dep[u] & 1 ? 1 : n;
	bool is = 1;
	for (int e = adj[u], v; e; e = nxt[e])
		if ((v = go[e]) != fu)
		{
			dfs(v, u); is = 0;
			if (dep[u] & 1) val[u] = Max(val[u], val[v]);
			else val[u] = Min(val[u], val[v]);
		}
	if (is) lea[val[u] = u] = 1, ans[0] = (ans[0] + ans[0]) % rqy;
}
void dfs1(int u, int fu, bool op)
{
	fa[u] = fu; sze[u] = 1; bel[u] = op; cnt[u] = lea[u];
	for (int e = adj[u], v; e; e = nxt[e])
		if ((v = go[e]) != fu && val[v] != val[1])
		{
			dfs1(v, u, op);
			sze[u] += sze[v]; cnt[u] += cnt[v];
			if (sze[v] > sze[son[u]]) son[u] = v;
		}
	f[u] = (op ? val[u] > val[1] : val[u] < val[1]) ? pw[cnt[u]] : 0;
	g[u].a = re[u] = 1;
	if ((dep[u] & 1) ^ op)
	{
		for (int e = adj[u], v; e; e = nxt[e])
			if ((v = go[e]) != fu && val[v] != val[1] && v != son[u])
			{
				g[u].a = 1ll * g[u].a * f[v] % rqy;
				if (f[v]) re[u] = 1ll * re[u] * f[v] % rqy; else c0[u]++;
			}
	}
	else
	{
		for (int e = adj[u], v; e; e = nxt[e])
			if ((v = go[e]) != fu && val[v] != val[1] && v != son[u])
			{
				int tmp = (pw[cnt[v]] - f[v] + rqy) % rqy;
				g[u].a = 1ll * g[u].a * tmp % rqy;
				if (tmp) re[u] = 1ll * re[u] * tmp % rqy; else c0[u]++;
			}
		g[u].b = (pw[cnt[u]] - 1ll * g[u].a * pw[cnt[son[u]]] % rqy + rqy) % rqy;
	}
	bot[u] = son[u] ? bot[son[u]] : u;
}
void dfs2(int u, int fu)
{
	if (son[u])
	{
		top[son[u]] = top[u];
		idx[pos[son[u]] = ++QAQ] = son[u];
		dfs2(son[u], u);
	}
	for (int e = adj[u], v; e; e = nxt[e])
		if ((v = go[e]) != fu && v != son[u] && val[v] != val[1])
			top[v] = v, idx[pos[v] = ++QAQ] = v, dfs2(v, u);
}
void build(int l, int r, int p)
{
	if (l == r) return (void) (gp[p] = g[idx[l]]);
	int mid = l + r >> 1;
	build(l, mid, p2); build(mid + 1, r, p3);
	gp[p] = gp[p2] * gp[p3];
}
void change(int l, int r, int pos, modi v, int p)
{
	if (l == r) return (void) (gp[p] = v);
	int mid = l + r >> 1;
	if (pos <= mid) change(l, mid, pos, v, p2);
	else change(mid + 1, r, pos, v, p3);
	gp[p] = gp[p2] * gp[p3];
}
modi ask(int l, int r, int s, int e, int p)
{
	if (e < l || s > r) return (modi) {1, 0};
	if (s <= l && r <= e) return gp[p];
	int mid = l + r >> 1;
	return ask(l, mid, s, e, p2) * ask(mid + 1, r, s, e, p3);
}
void modify(int u, int x)
{
	bool fi = 1; int of;
	while (1)
	{
		int v = top[u], w = fa[v], rf = f[v], nf;
		if (fi) fi = 0, f[u] = x; nf = f[v];
		if (v != bot[u]) nf = f[v] =
			ask(1, n, pos[v], pos[bot[u]] - 1, 1) * f[bot[u]] % rqy;
		if (!w) {of = rf; u = v; break;}
		if ((dep[v] & 1) ^ bel[v]) rf = (pw[cnt[v]] - rf + rqy) % rqy,
			nf = (pw[cnt[v]] - nf + rqy) % rqy;
		if (!rf) c0[w]--; else re[w] = 1ll * re[w] * qpow(rf, rqy - 2) % rqy;
		if (!nf) c0[w]++; else re[w] = 1ll * re[w] * nf % rqy;
		g[w].a = c0[w] ? 0 : re[w];
		g[w].b = (dep[w] & 1) ^ bel[w] ? 0 : (pw[cnt[w]] -
			1ll * g[w].a * pw[cnt[son[w]]] % rqy + rqy) % rqy;
		change(1, n, pos[w], g[w], 1);
		u = w;
	}
	if (of == pw[cnt[u]]) cnt0--; else rea = 1ll * rea *
		qpow(pw[cnt[u]] - of + rqy, rqy - 2) % rqy;
	if (f[u] == pw[cnt[u]]) cnt0++;
		else rea = 1ll * rea * (pw[cnt[u]] - f[u] + rqy) % rqy;
}
int main()
{
	int x, y;
	read(n); read(l); read(r);
	for (int i = 1; i < n; i++)
		read(x), read(y), add_edge(x, y);
	ans[0] = pw[0] = ans[n] = 1; dfs(1, 0);
	for (int i = 1; i <= n; i++) pw[i] = (pw[i - 1] + pw[i - 1]) % rqy;
	rea = 499122177ll * ans[0] % rqy;
	for (int u = 1; u <= n; u++)
	{
		if (val[u] != val[1] || u == val[1]) continue;
		dfs1(u, 0, dep[u] & 1);
		if (!cnt[u] || lea[u]) continue;
		idx[pos[u] = ++QAQ] = top[u] = u;
		dfs2(u, 0);
	}
	build(1, n, 1);
	for (int i = 1; i < n; i++)
	{
		if (i > 1 && val[1] >= i && lea[val[1] - i + 1] && bel[val[1] - i + 1])
			modify(val[1] - i + 1, 1);
		if (i > 1 && val[1] <= n - i + 1 && lea[val[1] + i - 1]
			&& !bel[val[1] + i - 1]) modify(val[1] + i - 1, 1);
		ans[i] = cnt0 ? 0 : rea;
	}
	for (int i = l; i <= r; i++) printf("%d ", (ans[i - 1] - ans[i] + rqy) % rqy);
	return puts(""), 0;
}
[LOJ#3044][动态DP]「ZJOI2019」Minimax 搜索的更多相关文章
- Loj #3044. 「ZJOI2019」Minimax 搜索
		Loj #3044. 「ZJOI2019」Minimax 搜索 题目描述 九条可怜是一个喜欢玩游戏的女孩子.为了增强自己的游戏水平,她想要用理论的武器武装自己.这道题和著名的 Minimax 搜索有关 ... 
- 【LOJ】#3044. 「ZJOI2019」Minimax 搜索
		LOJ#3044. 「ZJOI2019」Minimax 搜索 一个菜鸡的50pts暴力 设\(dp[u][j]\)表示\(u\)用\(j\)次操作能使得\(u\)的大小改变的方案数 设每个点的初始答案 ... 
- LOJ3044. 「ZJOI2019」Minimax 搜索
		LOJ3044. 「ZJOI2019」Minimax 搜索 https://loj.ac/problem/3044 分析: 假设\(w(1)=W\),那么使得这个值变化只会有两三种可能,比\(W\)小 ... 
- 「ZJOI2019」Minmax搜索
		传送门 Solution 叶子节点的变化区间是连续的,可得知非叶子节点的权值变化区间也是连续的 由此可知,\(W\)的变化值的可行域也是连续的,所以只需要看它能否变为\(W+1\)或\(W-1\) 对 ... 
- 「ZJOI2019」&「十二省联考 2019」题解索引
		「ZJOI2019」&「十二省联考 2019」题解索引 「ZJOI2019」 「ZJOI2019」线段树 「ZJOI2019」Minimax 搜索 「十二省联考 2019」 「十二省联考 20 ... 
- Loj #3042. 「ZJOI2019」麻将
		Loj #3042. 「ZJOI2019」麻将 题目描述 九条可怜是一个热爱打麻将的女孩子.因此她出了一道和麻将相关的题目,希望这题不会让你对麻将的热爱消失殆尽. 今天,可怜想要打麻将,但是她的朋友们 ... 
- 【LOJ】#3042. 「ZJOI2019」麻将
		LOJ#3042. 「ZJOI2019」麻将 如何判定一个集合牌有没有胡的子集是不是胡的 就用一个\(dp[j][k][0/1]\)表示有j个连续两个的串,有k个连续1个串,有没有对子,再记一下这个集 ... 
- loj#2537. 「PKUWC2018」Minimax
		题目链接 loj#2537. 「PKUWC2018」Minimax 题解 设\(f_{u,i}\)表示选取i的概率,l为u的左子节点,r为u的子节点 $f_{u,i} = f_{l,i}(p \sum ... 
- Loj #3045. 「ZJOI2019」开关
		Loj #3045. 「ZJOI2019」开关 题目描述 九条可怜是一个贪玩的女孩子. 这天,她和她的好朋友法海哥哥去玩密室逃脱.在他们面前的是 \(n\) 个开关,开始每个开关都是关闭的状态.要通过 ... 
随机推荐
- 性能测试基础-开门篇3(LR常用函数介绍)
			LR常用的函数,协议不一样函数会不一样,这里简单的介绍下HTTP\WEBSERVICE\SOCKET协议常用函数: HTTP: web_set_max_html_param_len("102 ... 
- maxmind geoip2使用笔记
			客户需求如下,nginx的访问日志中ip,匹配出对应的国家,省份和城市,然后给我了一个maxmind的连接参考. 查找资料,有做成hive udf的使用方式, 我们项目中一直使用 waterdrop ... 
- 学习linux命令,看这篇2w多字的linux命令详解
			用心分享,共同成长 没有什么比每天进步一点点更重要了 本文已收录到我的github:https://github.com/midou-tech/articles/tree/master/docs/li ... 
- 牛客训练赛55 E 树
			很妙的一个树形DP问题,简单考虑了一下就过了 https://ac.nowcoder.com/acm/contest/2927/E 主要就是推公式(公式有点长呀) 大概就是这样,其实挺简单的. #in ... 
- appium+android自动化测试环境部署
			1 node.js安装 官网(https://nodejs.org/en/) 下载对应版本的node.js并安装 安装完成后cmd中输入node -v,输入版本号则安装成功 2 jdk安装 下载对应版 ... 
- 20191031-6beta week 1/2 Scrum立会报告+燃尽图 04
			此作业要求参见https://edu.cnblogs.com/campus/nenu/2019fall/homework/9914 git地址:https://e.coding.net/Eustia/ ... 
- 关于有向图走“无限次”后求概率/期望的口胡/【题解】HNCPC2019H 有向图
			关于有向图走"无限次"后求概率/期望的口胡/[题解]HNCPC2019H 有向图 全是口胡 假了不管 讨论的都是图\(G=(V,E),|V|=n,|E|=m\)上的情况 " ... 
- Spring MVC拦截器配置
			Spring MVC拦截器配置 (1)自定义拦截器 package learnspringboot.xiao.other; import org.springframework.web.servlet ... 
- 「UVA1185」Big Number 解题报告
			UVA1185 Big Number In many applications very large integers numbers are required. Some of these appl ... 
- Codeforces Round #524 (Div. 2)(前三题题解)
			这场比赛手速场+数学场,像我这样读题都读不大懂的蒟蒻表示呵呵呵. 第四题搞了半天,大概想出来了,但来不及(中途家里网炸了)查错,于是我交了两次丢了100分.幸亏这次没有掉rating. 比赛传送门:h ... 
