[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\) 个开关,开始每个开关都是关闭的状态.要通过 ...
随机推荐
- NuGet 如何设置图标
在找 NuGet 的时候可以看到有趣的库都有有趣的图标,那么如何设置一个 NuGet 的图标 在开始之前,请在nuget官方网站下载 NuGet.exe 同时设置环境变量 环境变量设置的方法就是将 N ...
- 【hdu 1848】Fibonacci again and again
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s) ...
- java面试代码题
1.阅读 Shape 和 Circle 两个类的定义.在序列化一个 Circle 的对象 circle 到 文件时,下面哪个字段会被保存到文件中? 文件时,下面哪个字段会被保存到文件中? A. nam ...
- <QluOJ2018NewCode>约数个数
题目描述 p^q表示p的q次方,正整数M可以分解为M=(p1^a1)*(p2^a2)*(p3^a3)*……*(pn^an)的形式,其中p1,p2……pn为质数(大于1并且只能被1和自身整除的数叫做质数 ...
- 【u033】地震逃生
Time Limit: 1 second Memory Limit: 64 MB [问题描述] 汶川地震发生时,四川**中学正在上课,一看地震发生,老师们立刻带领x名学生逃跑,整个学校可以抽象地看成一 ...
- QString 转换为 char *
一.QString 转换为 char * 将 QString 转 char *,需要用到 QByteArray 类,QByteArray 类的说明详见 Qt 帮助文档. 因为 char * 最后都有一 ...
- markdown color
深青色 深蓝色 seagreen darkgreen indianred cornflowerblue lightskyblue coral lightcoral darkorange
- 第二阶段:2.商业需求文档MRD:5.MRD-Roadmap及规划
产品路线图可以用泳道图来实现.将之前做过的泳道图的角色换为阶段即可. 可以以月为单位.左边就是一些产品的功能. 基础功能,有的功能会跨月甚至夸功能模块.比如图中的会员等级. 通过线段来联系各个功能与先 ...
- 一文带你了解 OAuth2 协议与 Spring Security OAuth2 集成!
OAuth 2.0 允许第三方应用程序访问受限的HTTP资源的授权协议,像平常大家使用Github.Google账号来登陆其他系统时使用的就是 OAuth 2.0 授权框架,下图就是使用Github账 ...
- ansible批量部署tomcat
1.1 构建目录结构 此操作是安装nginx+mysql+tomcat+db的目录结构,可以参考一下,不错~ mkdir -p /ansible/roles/{nginx,mysql,tomcat,d ...