VP Educational Codeforces Round 54 (Rated for Div. 2)


A. Minimizing the String

题意:删除一个字符使得字符串字典序最小。

经典结论,删第一个\(s_i > s_{i+1}\)

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
    for (int i = 0; i + 1 < n; ++ i) {
    	if (s[i] > s[i + 1]) {
    		std::cout << s.substr(0, i) + s.substr(i + 1) << "\n";
    		return;
    	}
    }

    std::cout << s.substr(0, n - 1) << "\n";
}

B. Divisor Subtraction

题意:每次把\(n\)减去它的最小质因子,直到零,求操作次数。

显然偶数一直减\(2\),奇数减掉一个质因子后变成偶数,也一直减\(2\)
那么我们找到第一个质因子就行。

点击查看代码
void solve() {
    i64 n;
    std::cin >> n;
    int m = 1e5;
    for (int i = 2; i <= n / i; ++ i) {
    	if (n % i == 0) {
    		std::cout << (n - i) / 2 + 1 << "\n";
    		return;
    	}
    }

    std::cout << 1 << "\n";
}

C. Meme Problem

题意:给你\(d\),找两个非负数\(a, b\),使得\(a+b = d, a\times b = d\)

可得\(a\times(d - a) = d\)。得到方程\(a^2 - d\times a + d = 0\)
解这个方程看有没有非负数解。

点击查看代码
void solve() {
    std::cout << std::fixed << std::setprecision(12);
    int d;
    std::cin >> d;
    //ad - a^2 = d;
    //a^2 - ad + d = 0
    if (d == 0) {
    	std::cout << "Y " << 0.0 << " " << 0.0 << "\n";
    	return;
    }
    double x = std::sqrt(d * d - 4 * d);
    if ((d + x) > 0) {
    	std::cout << "Y " << (d + x) / 2 << " " << d - (d + x) / 2 << "\n";
    } else if ((-d + x) > 0) {
    	std::cout << "Y " << (-d + x) / 2 << " " << d - (-d + x) / 2 << "\n";
    } else {
    	std::cout << "N\n";
    }
}

D. Edge Deletion

题意:给你一个图,删边直到边数小于等于\(k\)。使得这个图的最短路和原图的最短路相同的点最多。

最短路径树。
最短路径树就是\(dijkstra\)过程中记录每个点的前继边就行了。然后没在最短路径树上的边都可以删,如果还有删,就在树上\(dfs\)\(k\)条边。

点击查看代码
void solve() {
    int n, m, k;
    std::cin >> n >> m >> k;
    std::vector<std::vector<std::array<int, 3>>> adj(n);
    std::vector<int> st(m);
    for (int i = 0; i < m; ++ i) {
    	int u, v, w;
    	std::cin >> u >> v >> w;
    	-- u, -- v;
    	adj[u].push_back(std::array<int, 3>{v, w, i});
    	adj[v].push_back(std::array<int, 3>{u, w, i});
    }

    std::vector<int> pre(n, -1);
    using PII = std::pair<i64, int>;
    const i64 inf = 1e18;
    std::priority_queue<PII, std::vector<PII>, std::greater<PII>> heap;
    std::vector<i64> dist(n, inf);
    dist[0] = 0;
    heap.emplace(dist[0], 0);
    while (heap.size()) {
    	auto [d, u] = heap.top(); heap.pop();
    	if (d != dist[u]) {
    		continue;
    	}

    	for (auto & [v, w, id] : adj[u]) {
    		if (dist[v] > dist[u] + w) {
    			dist[v] = dist[u] + w;
    			pre[v] = id;
    			heap.emplace(dist[v], v);
    		}
    	}
    }

    for (int i = 0; i < n; ++ i) {
    	if (pre[i] != -1) {
    		st[pre[i]] = 1;
    	}
    }

    k = std::min(n - 1, k);
    std::vector<int> ans;
    auto dfs = [&](auto & self, int u, int fa) -> void {
    	if (k == 0) {
    		return;
    	}

    	for (auto & [v, w, id] : adj[u]) {
    		if (v == fa || !st[id]) {
    			continue;
    		}

    		if (k == 0) {
    			return;
    		}

    		if (st[id]) {
    			ans.push_back(id);
    			-- k;
    			self(self, v, u);
    		}
    	}
    };

    dfs(dfs, 0, -1);

    std::cout << ans.size() << "\n";
    for (auto & x : ans) {
    	std::cout << x + 1 << " \n"[x == ans.back()];
    }
}

E. Vasya and a Tree

题意:给你一棵树,每次给\(u_i\)节点的子树里距离它小于等于\(d_i\)的点加上\(x_i\)。最后求每个点的值。

考虑求出深度,那么每个点需要在它的子树的\([dep_u, dep_u + d_i]\)这个区间的点加上\(x_i\)。可以直接\(dfs\),到一个点就把它的操作加上,然后递归子树,离开的时候把操作撤销就行了。这是一个区间修改单点查询问题。可以用树状数组做。

点击查看代码
template <class T>
struct Fenwick {
    int n;
    std::vector<T> tr;

    Fenwick(int _n) {
        init(_n);
    }

    void init(int _n) {
        n = _n;
        tr.assign(_n + 1, T{});
    }

    void add(int x, const T &v) {
        for (int i = x; i <= n; i += i & -i) {
            tr[i] = tr[i] + v;
        }
    }

    T query(int x) {
        T res{};
        for (int i = x; i; i -= i & -i) {
            res = res + tr[i];
        }
        return res;
    }

    T sum(int l, int r) {
        return query(r) - query(l - 1);
    }
};

void solve() {
    int n;
    std::cin >> n;
    std::vector<std::vector<int>> adj(n);
    for (int i = 1; i < n; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
    }

    int m;
    std::cin >> m;
    std::vector<std::vector<std::pair<int, int>>> op(n);
    for (int i = 0; i < m; ++ i) {
    	int v, d, x;
    	std::cin >> v >> d >> x;
    	-- v;
    	op[v].emplace_back(d, x);
    }

    std::vector<int> dep(n);
    std::vector<i64> ans(n);
    Fenwick<i64> tr(n + 1);
    auto dfs = [&](auto & self, int u, int fa) -> void {
    	for (auto & [d, x] : op[u]) {
    		tr.add(dep[u], x);
    		tr.add(std::min(n, dep[u] + d) + 1, -x);
    	}

    	ans[u] = tr.query(dep[u]);
    	for (auto & v : adj[u]) {
    		if (v == fa) {
    			continue;
    		}

    		dep[v] = dep[u] + 1;
    		self(self, v, u);
    	}

    	for (auto & [d, x] : op[u]) {
    		tr.add(dep[u], -x);
    		tr.add(std::min(n, dep[u] + d) + 1, x);
    	}
    };

    dep[0] = 1;
    dfs(dfs, 0, -1);
    for (int i = 0; i < n; ++ i) {
    	std::cout << ans[i] << " \n"[i == n - 1];
    }
}

F. Summer Practice Report

题意:你要构造\(n\)个字符串,每个字符串有\(x_i\)\(T\)\(y_i\)\(F\)。然后把他们按顺序拼接为一个字符串。能不能使得这个字符串没有一个长度大于\(k\)的子串都是相同字符。

贪心加\(dp\)
考虑\(dpx_i\)为前\(i\)个字符串结尾为\(T\)最短的连续个数。\(dpy_i\)表示\(F\)结尾的最短个数。
那么对于第\(i\)位,我们有\(dpx_{i-1} + x_i\)\(T\),要把他们切割为合法的字符串,至少需要\(\lceil \frac{dpx_{i-1} + x_i}{k} \rceil - 1\)\(F\)。如果\(y_i\)少于这个数无解。如果\(y_i\)等于这个数,\(dpx_i = dpx_{i-1} + x_i - (\lceil \frac{dpx_{i-1} + x_i}{k} \rceil - 1) \times k\)。否则\(dpx_i = 0\)\(dpy_i\)的讨论同理。

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<i64> x(n), y(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> x[i];
    }

    for (int i = 0; i < n; ++ i) {
    	std::cin >> y[i];
    }

    i64 dpx = 0, dpy = 0;
    for (int i = 0; i < n; ++ i) {
    	i64 xcnt = (dpx + x[i] + k - 1) / k - 1;
    	if (y[i] < xcnt) {
    		std::cout << "NO\n";
    		return;
    	}

    	i64 ndpx = y[i] > xcnt ? 0 : dpx + x[i] - xcnt * k;

    	i64 ycnt = (dpy + y[i] + k - 1) / k - 1;
    	if (x[i] < ycnt) {
    		std::cout << "NO\n";
    		return;
    	}

    	i64 ndpy = x[i] > ycnt ? 0 : dpy + y[i] - ycnt * k;

    	dpx = ndpx;
    	dpy = ndpy;
    }

    std::cout << "YES\n";
}
posted @ 2025-04-08 05:17  maburb  阅读(15)  评论(0)    收藏  举报