【题解】$test0627 $ 数据读取问题

题目描述

(\)


(\)

(Solution)

这题目描述就离谱bushi

考场翻来覆去读了几遍题没看懂kkkkk

大概翻译一下题目意思就是:给你一个序列 (d),如果当前读到 (d_i) ,就跳到 (d_i + a_i) ,目标是最后正好跳到 (n + 1) ;对于 (a_i) 可以修改为 (a_i +- y) ,修改代价为 (y(y >= 0)) ;求最小代价。现在有一棵树,对于从根节点到叶子节点的每条链都看做一个序列,求出所有最小代价。

感谢(mathtt{I})(mathtt{color{red}{makf}}) 给我讲了好几遍这道题,终于明白力!!!

(\)

考虑只在序列上做

(dp[i]) 表示跳到了 (i) 的时候的最小代价

若当前 (dp[i]) 已知,设 (j = i + a_i) ,则 (dp[j] = min(dp[j], dp[i])) ,因为可以直接跳到,不需要修改 (a_i)

那么来考虑修改,则 $$dp[j - x] = min(dp[j - x], dp[j] + j - x)$$ $$dp[j + x] = min(dp[j + x], dp[j] + x - j)$$

于是可以 (O(n^2)) 顺利解决

(\)

接下来考虑优化

先只考虑 (dp[j] = min(dp[j], dp[i] + i - j)) 这一种转移(就是上面写的那个式子,改了一下变量),往后推的和这个差不多

想把 (O(n^2)) 降为 (O(nlog n))正确做法是在脑子里随便找一种数据结构试试可不可行

直接给结论:开一颗线段树,在第 (i) 个叶子节点存的是 (tree[i] = dp[i] + i) ,维护最小值

为什么要存这个?其实很巧妙

看看上面的转移方程,如果先不考虑取 (min),即 $$dp[j] = dp[i] + i - j$$

得到了 (dp[j]) 后我们要存进线段树中,将方程两边加上 (j) 就变成了 $$tree[j] = dp[j] + j = dp[i] + i - j + j = dp[i] + i = tree[i]$$

发现直接可以用线段树中的 (tree[i]) 更新 (tree[j])

(\)

考虑更新的顺序

由于每个点只能往后跳,所以可以直接按照序列顺序更新

简单证明:若现在已经更新到了 (i) ,假设点 (j > i) ,若想用 (j + a_j) 更新 (i) ,相当于把 (a_j) 更改为 (i - j) ,而 (a_j) 不可能是负的,所以无法更新

(\)

考虑在树上做

直接深搜,维护一个线段树,在回溯时撤销操作即可

多写一句关于在树上维护某个数据结构进行撤销操作怎么做

以下所有原数组指的是全局变量的数据结构

(1.) 直接开一个数组存下这个原数组,再把原数组进行更新,最后再把这个数组的值赋给原数组

(2.) 对于树状数组/线段树,如果是单点修改,那就最多只有 (log n) 个点更改,保存这些点即可

(3.) 对于树状数组/线段树,如果是单点修改,可以直接存下在原数组里这个点的值,撤销就用这个值修改一下(比如说在这个点上 (+3) ,撤销就在这个点上 (-3)

(\)

完结撒花✿✿ヽ(°▽°)ノ✿

(update(2020.7.2):) 关于线段树中到了叶子节点直接赋值而不用取 (min) ,是因为在修改之前就判断了是否大于当前的值,所以一定要修改。被踩辣 (kk)

(\)


(\)

(Code)

#include<bits/stdc++.h>
#define mid (l + r >> 1)
#define ls (k << 1)
#define rs (k << 1| 1)
#define F(i, x, y) for(int i = x; i <= y; ++ i)
using namespace std;
int read();
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;
int n, x, y, m;
int a[N], ans[N], dp[N], tree[2][N << 2];
int head[N], cnt, ver[N << 1], nxt[N << 1];
void add(int x, int y){ver[++ cnt] = y, nxt[cnt] = head[x], head[x] = cnt;}
void update(int l, int r, int k, int pos, int v, int op)
{
	if(l == r && l == pos) {tree[op][k] = v; return;}
	if(pos <= mid) update(l, mid, ls, pos, v, op);
	else update(mid + 1, r, rs, pos, v, op);
	tree[op][k] = min(tree[op][ls], tree[op][rs]);
}
int query(int l, int r, int k, int x, int y, int op)
{
	if(l >= x && r <= y) return tree[op][k];
	int res = inf; 
	if(x <= mid) res = min(res, query(l, mid, ls, x, y, op));
	if(y > mid) res = min(res, query(mid + 1, r, rs, x, y, op));
	return res;
}
void dfs(int x, int dep)
{
	int tmp1 = -inf, tmp2 = -inf, u = a[x] + dep, now = dp[dep - 1] + 1;
	if(x != 1)
	{
		int b1 = query(1, n, 1, u, u, 0), b2 = query(1, n, 1, u, u, 1);
		if(b1 > now - u) tmp1 = b1, update(1, n, 1, u, now - u, 0);
		if(b2 > now + u) tmp2 = b2, update(1, n, 1, u, now + u, 1);
		dp[dep] = min(query(1, n, 1, 1, dep, 0) + dep, query(1, n, 1, dep, n, 1) - dep);
	}
	for(int i = head[x]; i; i = nxt[i]) dfs(ver[i], dep + 1);
	if(! head[x]) ans[++ m] = dp[dep];
	if(tmp1 != -inf) update(1, n, 1, u, tmp1, 0);
	if(tmp2 != -inf) update(1, n, 1, u, tmp2, 1);
}
int main()
{
	n = read();
	F(i, 1, n) 
	{
		a[i] = read(), y = read();
		F(j, 1, y) x = read(), add(i, x);
	}
	memset(tree, 0x3f, sizeof(tree));
	dfs(1, 1), sort(ans + 1, ans + m + 1);
	F(i, 1, m) printf("%d
", ans[i]);
	return 0;
}
int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * f;
}
原文地址:https://www.cnblogs.com/Bn_ff/p/13203529.html