zoukankan      html  css  js  c++  java
  • [LOJ#3044][动态DP]「ZJOI2019」Minimax 搜索

    • 题目传送门

    • 容易想到一种暴力 DP:先转化成对于每个 (k) 求出 (max_{iin 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_{vin son[u]}(2^{cnt_v}-f[v]) ]

    • 否则:

    • [f[u]=prod_{vin 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(nlog^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;
    }
    
  • 相关阅读:
    python-opencv-绘图函数
    sublime下package control安装无效解决
    关于vue.js中slot的理解
    关于vue.js父子组件数据传递
    oAuth2.0及jwt介绍
    nodejs文件上传组件multer使用
    nodejs设置服务端允许跨域
    添加swagger api文档到node服务
    关于for循环里面异步操作的问题
    nodejs记录2——一行代码实现文件下载
  • 原文地址:https://www.cnblogs.com/xyz32768/p/12040207.html
Copyright © 2011-2022 走看看