题有点多 | 2023.6.23 做题记录
题有点多 | 2023.6.23 做题记录
CF1805D A Wide, Wide Graph
每个节点是否被删只用考虑离其最远的节点
好直觉啊
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <string>
#include <random>
typedef long long ll;
typedef unsigned long long ull;
using std::cin;
using std::cout;
using std::pair;
template<typename T, typename Ts>
inline void chkmin(T &a, Ts b) {if(a > b) a = b;}
template<typename T, typename Ts>
inline void chkmax(T &a, Ts b) {if(a < b) a = b;}
namespace Nostalgia {
const int N = 2e5 + 10;
const ll mod = 998244353;
int n, tp, bt;
//Save Graph
std::vector<int> G[N >> 1];
struct edge {
int u, v;
edge(){}
edge(int _u, int _v) {u = _u, v = _v;}
}E[N];
int idx;
inline void add(int u, int v) {
G[u].push_back(idx), E[idx++] = edge(u, v);
}
int dep[N >> 1] = {-1}, mxdis[N >> 1];
//
void dfs(int u, int fa) {
dep[u] = dep[fa] + 1;
if(dep[u] > dep[tp])
tp = u;
chkmax(mxdis[u], dep[u]);
for(int i : G[u]) {
edge e = E[i];
if(e.v == fa)
continue;
dfs(e.v, u);
}
}
void solve() {
cin >> n;
for(int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
add(u, v), add(v, u);
}
dfs(1, 0);
dfs(tp, 0);
dfs(tp, 0);
std::sort(mxdis + 1, mxdis + 1 + n);
int p = 1;
int ans = 1;
for(int k = 1; k <= n; k++) {
while(k - 1 == mxdis[p]) p++, ans++;
cout << std::min(ans, n) << ' ';
}
}
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
while(T--) {
Nostalgia::solve();
}
return 0;
}
CF1795E. Explosions?
这啥啊。
咋想了一个多小时,我这么菜的吗?
是线段树优化DP
不妨假设 \(f_i\) 表示从 \(i\) 起爆,只考虑左边所需要的最小能量,那么我们动态维护一个单调的序列(起爆的时候令答案最小的序列)
那么如果 \(a_i>a_{i-1}\),那么显然有 \(f_i=f_{i-1}\)
如果 \(a_i<a_{i-1}\),那么我们要将 \(j\in [1, i-1]\) 中的所有值减去 \(a_{i-1}-a_i+1\),然后和 \(0\) 取 \(\max\),具体实现可以线段树上二分,我们可以算出这个时候的代价,记为 \(t\),则
\[f_i=f_{i-1}+t
\]
随后我们对称的维护一个 \(g_i\) 表示只考虑右边的最小能量,答案就是
\[\min_{i=1}^{n}(f_i+g_i+h_i)
\]
因为懒了不想写双重tag的线段树,其实维护最右/左边为 \(0\) 的数然后暴力删复杂度是对的