Codeforces Round #787 (Div. 3) F, G题题解

Codeforces题解汇总

Codeforces Round #787 (Div. 3)

F. Vlad and Unfinished Business ( \(\color{#AAF}{1800}\) )

题意

给了点数为 \(n\) 的树,和 \(k\) 个必须到达的点,出发的起点 \(x\) 和 到达终点 \(y\),边权为 \(1\) ,询问从起点经过指定的
\(k\) 个点,最后到达 \(y\) 的最短路径花费, \(k\) 个点的访问顺序可以任意。

数据范围
\(1\leq k \leq n \leq 2 * 10^5\)

思路

  • 贪心地想,对于所有任务点必须到,然后要返回,那么起点到这些点的路径和至少为2倍到达这些点的花费。
  • 问题转化为,经过 \(k\) 个点的"最小生成树",当然这里不需要最小生成树算法,只需要知道我们不需要经过的点数即可,剩余点数为 \(x\),路径长度为 \(x-1\)
  • 那么现在访问了所有点,要更贪心的想,从 \(x->y\) 我们是不是只用走一次就行了,先把 \(y\) 以外的点搜完,最后来搜 \(y\) 的子树即可,那么 \(path:x->y\) 只需要经过一次。
  • 所以最后答案为 \(2*(必经路径上点数-1) - x->y的深度(距离)\) ,用 dfs 求子树标记的方式来解,详细见代码,时间复杂度 \(O(n)\)

Solution

#include<bits/stdc++.h>
typedef long long ll;
typedef std::pair<int, int> PII;
typedef std::pair<ll, ll> PLL;
typedef unsigned long long ull;
#define x first
#define y second
#define pb push_back
#define mkp make_pair
#define endl "\n"
using namespace std;
const int N = 2e5 + 10;
int n, k, x, y, ans;
vector<int> edge[N];
int dep[N];
bool st[N];

int dfs(int u, int p){
	int tot = 0;
	for(auto v: edge[u]){
		if(v == p) continue;
		dep[v] = dep[u] + 1;
		tot += dfs(v, u);
	}
	if(!tot && !st[u]) ans--;
	return tot + (st[u] == true);
}

int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while(T--){
		cin >> n >> k;
		cin >> x >> y;
		ans = n;
		vector<int> a(k + 1, 0);
		for(int i = 1; i <= k; i++){
			cin >> a[i];
			st[a[i]] = true;
		}
		st[x] = true, st[y] = true;
		for(int i = 1; i < n; i++){
			int u, v;
			cin >> u >> v;
			edge[u].pb(v);
			edge[v].pb(u);
		}
		dfs(x, -1);
		cout << 2 * (ans - 1) - dep[y] << endl;
		for(int i = 1; i <= n; i++){
			edge[i].clear();
			st[i] = false;
			dep[i] = 0;
		}
	}
    return 0;
}

G. Sorting Pancakes ( \(\color{#FB5}{2300}\) )

参考题解: Ximena, 严格鸽

题意
给了一个长度 \(n\) 的数组 \(a\) ,和保证为 \(m\), 每次只能将 \(a_i\) 减 1, \(a_{i-1}\; or\; a_{i+1}\) 加 1,询问最小的操作次数使 \(a\) 非严格递减。

数据范围
\(1\leq n,m \leq 250\)

思路

  • 毫无疑问,一道dp题,根据数据范围,大概可以容忍 \(O(n^3)\) 级别的时空复杂度。
  • 在设计状态前,务必需要一个结论
    • 对于这样的相邻移动元素的操作而言,设操作后的结果为 \(b\) 数组,总操作次数等于 \(\Sigma_{i=1}^n(|S_b[i] - S_a[i]|)\) ,对应前缀和差的绝对值的和
  • 首先dp的阶段是长度 \(i\),由于数组和固定,考虑加入一维当前数组的和 \(j\) ,由于要满足递增或者递减,加入一维记录最后一个数 \(k\) ,由于需要对比大小,这个数其实是一个最值。
  • 那么状态已经设计出来了: dp[i][j][k] 表示数组前 \(i\) 位,和为 \(j\) ,最后一位最值为 \(k\) 的最小操作次数。
  • 考虑如何转移方便,如果按照题意原本描述来看,转移方程是遍历前一个数 \(x,k\leq x\) dp[i][j][k] = min(dp[i][j][k], dp[i - 1][j - k][x] + abs(j - s[i])),观察发现可以使用最小后缀来优化 \(x\) 的遍历,此时预处理最小后缀复杂度为 \(O(n^3)\)
  • 而考虑反着做,即将数组翻转后,考虑将数组构造成非严格递增。那么 \(k\) 代表的是最后一位的最大值。从小到大枚举\(k\) 时,需要的是最小前缀,可以很方便的记录, 如下代码所示
for(int i = 1; i <= n; i++){
    for(int j = 0; j <= m; j++){
        int mi = 0x3f3f3f3f;
        for(int k = 0; k <= m - j; k++) {
            mi = min(mi, dp[i - 1][j][k]);      // 转移 k 时,mi 记录了上一位数值为 [0, k] 的最小值,因此是合法的
            f[i][j + k][k] = min(f[i][j + k][k], mi + abs(s[i] - abs(j + k)));
        }
    }
}
  • 时间复杂度:将 \(n\)\(m\) 同级,\(O(n^3)\) ,翻转数组的方法还没有想到如何优化成 \(O(n^2logn)\)
    • 如果尝试采用记录后缀最小来优化,发现对于下标 \(i\) 上每个数不能超过 m / i ,否则它一定比前面的某个数大(平均值),加入循环条件中可以做到 \(O(n^2logn)\)

Solution

#include<bits/stdc++.h>
typedef long long ll;
typedef std::pair<int, int> PII;
typedef std::pair<ll, ll> PLL;
typedef unsigned long long ull;
#define x first
#define y second
#define pb push_back
#define mkp make_pair
#define endl "\n"
using namespace std;
const int N = 260;
int n, m;
int a[N], s[N];
int dp[N][N][N];		
// 将数组翻转
// dp[i][j][k] 前i个数,和为j,最后一个数最大值为k 满足递增序列最小操作
int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> n >> m;
	for(int i = n; i >= 1; i--)
		cin >> a[i];
	for(int i = 1; i <= n; i++)
		s[i] = s[i - 1] + a[i];
	memset(dp, 0x3f, sizeof dp);
	dp[0][0][0] = 0;        // 前0个数,和为0,数最大值为0,操作次数为0
	for(int i = 1; i <= n; i++){
		for(int j = 0; j <= m; j++){
			int mi = 0x3f3f3f3f;
			for(int k = 0; k <= m - j; k++){
				mi = min(dp[i - 1][j][k], mi);
				dp[i][j + k][k] = min(dp[i][j + k][k], mi + abs((j + k) - s[i]));
			}
		}
	}
	int ans = 0x3f3f3f3f;
	for(int i = 0; i <= m; i++)
		ans = min(ans, dp[n][m][i]);
	cout << ans << endl;
    return 0;
}
posted @ 2022-05-07 13:45  Roshin  阅读(144)  评论(0编辑  收藏  举报
-->