Educational Codeforces Round 102 (Rated for Div. 2) 题解

题面

A.Replacing Elements

【题面】

给定一个有 \(n\) 个数字的数组 \(a\)。每次可以让 \(a_i\) 变成 \(a_j + a_k\)\(i \neq j,j \neq k,k \neq i\))。
判断是否可以让所有 \(a_i \leq d\)

【思路】

1.首先可以发现最优方案一定是让每一个 \(a_i\) 最小,那么除了 \(a_i\) 自己,最小的方案就是让 \(a_i\) 变成最小的 \(a_j+a_k\)
保证 (\(i \neq j,j \neq k,k \neq i\))。
2.那么在考虑 No 的情况,如果说对于最小的两个元素 \(a_i,a_j\),如果 \(a_i+a_j > d\) 那么一定无解。

【实现】

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
inline int read(){
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9'){ x = x * 10 + (ch ^ 48); ch = getchar(); }
	return x * f;
}
void solve(){
    int n = read(), d = read();
    vector<int>a(n+1);
    bool flag = true;
    for(int i=1; i<=n; ++i){
        a[i] = read();
        if (a[i] > d) flag = false;
    }
    if(flag){
        cout << "Yes\n";
        return ;
    }
    sort(a.begin()+1, a.end());
    if(a[1] + a[2] <= d){
        cout << "Yes\n";
    }
    else{
        cout << "No\n";
    }
    return ;
}
int main(){
    int T = read();
    while(T--){
        solve();    
    }
    return 0;
}

B.String LCM

【题面】

定义一个字符串的乘法,然后求出对于两个字符串 \(s\)\(t\) 求出其 \(lcm\)

【思路】

1.首先如果任意两个字符串存在一个 \(lcm\),那么其长度必然也满足 \(lcm\)
2.我们可以先求出两个字符串长度的 \(lcm\) 然后再用长的求出两个的 \(lcm\) 需要重复当前字符串几次。
3.把两个字符串分别重复这么多次,然后判断是否相等即可。

【实现】

#include <bits/stdc++.h>
using namespace std;
int gcd(int a, int b){
    return (b==0 ? a : gcd(b, a % b));
}
void solve(){
    string s, t;
    cin >> s >> t;
    int n = s.size(), m = t.size();
    int g = gcd(n, m);
    int lcm = g * (n/g) * (m/g);
    int n1 = lcm/n, m1 = lcm/m;
    string a="", b="";
    for(int i=1; i<=n1; ++i){
        a += s;
    }
    for(int i=1; i<=m1; ++i){
        b += t;
    }
    if(a==b){
        cout << a << '\n';
    }
    else{
        cout << -1 << '\n';
    }
    return ;
}
int main(){
    int T = read();
    while(T--){
        solve();    
    }
    return 0;
}

C.No More Inversions

【题面】

先说一下大致题意,就是要求一个排列 \(p\),然后对于给定数列把每个数的值用 \(p\) 对应的值替换,满足某个要求(好像是逆序对数不大于原数列)的情况下字典序最大。

【思路】

1.要计算原序列的逆序对数量,其结果为:

\[\frac{(n-k)\times(n-k+1)}{2} \]

由于排列 \(p\) 的长度为 \(k\),因此有 \(b[i] = p[a[i]] = p[i]\)。为了使排列 \(p\) 的字典序最大,根据康托展开的相关思想,需要让 \(p\) 的逆序对数最大化,即达到:

\[\frac{(n-k)\times(n-k+1)}{2} \]

2.如果直接使用逆康托展开来构造该排列,时间复杂度为 \(O(Tn\log n)\),显然会超时。
3.进一步观察可以发现,原序列的逆序对数量呈现等差数列的规律,因此猜想排列 \(p\) 的构造也可以利用类似的结构。具体来说,可以构造排列 \(p\) 为:

\[p = k, k-1, \ldots, k-t, 1, 2, \ldots, k-t-1 \]

此时 \(p\) 的逆序对数为:

\[\frac{(k-t)\times(k-t+1)}{2} \]

\(k - t = n - k\),解得 \(t = 2k - n\)。由此,该问题得到解决。

【实现】

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
inline int read(){
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9'){ x = x * 10 + (ch ^ 48); ch = getchar(); }
	return x * f;
}
void solve(){
    ll n = read(), k = read();
    for(int i=1;i<2*k-n;++i){
        cout << i << ' ';
    }
    int m=k;
    for(int i=2*k-n;i<=k;++i) {
        cout << m-- << ' ';
    }
    cout << '\n';
    return ;
}
int main(){
    int T = read();
    while(T--){
        solve();    
    }
    return 0;
}

D. Program

【题面】

给定 \(n\) 个操作和一个变量 \(x\),每一个操作满足:
\(x\)\(1\)
\(x\)\(1\)
问如果忽略操作 \(l\) 到 操作 \(r\) 之间的操作,那么 \(x\) 会有多少个不同的值

【思路】

1.在一段操作中 \(x\) 的所有值必然是连续的,所以只需求出最大值和最小值就知道了值域大小。无视一段操作后整个区间被分为至多两部分,只需求出每个值域的大小再减去公共部分即可。

【实现】

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
inline int read(){
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9'){ x = x * 10 + (ch ^ 48); ch = getchar(); }
	return x * f;
}
const int maxn = 2e5 + 9;
const int inf = 0x3f3f3f3f;
int sum[maxn],  maxxl[maxn], maxxr[maxn], minnl[maxn], minnr[maxn];
void solve() {
    int n=read(), q=read();
    string s;
    cin >> s;
    for (int i = 1; i <= n; ++i){
        sum[i] = sum[i - 1] + (s[i - 1] == '-' ? -1 : 1);
        maxxl[i] = max(maxxl[i - 1], sum[i]);
        minnl[i] = min(minnl[i - 1], sum[i]);
    }
    maxxr[n + 1] = -inf;
    minnr[n + 1] = inf;
    for (int i = n; i >= 1; --i){
        maxxr[i] = max(maxxr[i + 1], sum[i]);
        minnr[i] = min(minnr[i + 1], sum[i]);
    }
    for (int cas = 1, l, r; cas <= q; ++cas){
        cin >> l >> r;
        int t = sum[r] - sum[l - 1];
        int ans = max(maxxl[l - 1], maxxr[r + 1] - t) - min(minnl[l - 1], minnr[r + 1] - t) + 1;
        cout << ans << '\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T=read();
    while(T--) solve();
    return 0;
}

E.Minimum Path

【题面】

给定一张 \(n\) 个点 \(m\) 条边的无向图,第 \(i\) 条边边权为 \(w_i\)
对于一条从点 \(1\) 到点 \(n\) 的路径,设其经过的边集为 \(E\),定义这条路径的长度为:

\[\sum_{e \in E} w_e - \max_{e \in E} (w_e) + \min_{e \in E} (w_e) \]

求点 \(1\) 到其他所有点的最短路径。

【思路】

1.这种路径长度涉及最大值和最小值,无法直接进行转移,考虑将题目进行变形。题目要求计算:

\[\min \left\{ \sum_{e \in E} w_e - \max_{e \in E} (w_e) + \min_{e \in E} (w_e) \right\} \]

2.为了使得路径长度最小,需要减去的一定是边权最大值,加上的一定是边权最小值,因此题目实际上可以转化为:路径长度之和加上任意某边的最小值,再减去任意某边的最大值。
3.接下来可以通过建立分层图来实现加减操作。每层包含正常的连边,从第一层到第二层连接权值为 \(0\) 的边,从第二层到第三层连接权值为 \(2w\) 的边,答案即为从起点 \(1\) 到第三层各点的最短路径。
4.但上述方法忽略了先进行加法后进行减法的情况,因此可以再增加一层:从第一层到该新层连接权值为 \(2w\) 的边,再从该新层向终点层连接权值为 \(0\) 的边。

【实现】

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
inline int read(){
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9'){ x = x * 10 + (ch ^ 48); ch = getchar(); }
	return x * f;
}
const int N = 1e6 + 10;
int n, m, T;
struct edge {
	int to, nxt; ll val;
}e[N * 3 << 1];
int head[N], tot;
void add (int u, int v, ll w) {
	e[++tot] = (edge) { v, head[u], w }, head[u] = tot;
}

struct node {
	int val; ll key;
	bool operator < (const node &a) const {
		return key > a.key;
	}
};

priority_queue <node> q;

ll dis[N]; 
bool vis[N];
void dij () {
	memset (dis, 127 / 3, sizeof dis);
	dis[1] = 0;
	q.push((node){ 1, 0 });
	while (!q.empty()){
		int u = q.top().val; q.pop();
		if (vis[u]) continue;
		vis[u] = 1;
		for (int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			if (dis[v] > dis[u] + e[i].val) {
				dis[v] = dis[u] + e[i].val;
				q.push((node) { v, dis[v] });
			}
		}
	}
}

int main() {
	n = read(), m = read();
	for (int i = 1; i <= m; i++) {
		int u = read(), v = read(); ll w = read();
		add (u, v, w), add(v, u, w);
		add (u + n, v + n, w), add(v + n, u + n, w);
		add (u + 2 * n, v + 2 * n, w), add (v + 2 * n, u + 2 * n, w);
		add (u + 3 * n, v + 3 * n, w), add (v + 3 * n, u + 3 * n, w);
		
		add (u, v + n, 2 * w), add (v, u + n, 2 * w);
		add (u + n, v + 3 * n, 0), add(v + n, u + 3 * n, 0);
		
		add (u, v + 2 * n, 0), add (v, u + 2 * n, 0);
		add (u + 2 * n, v + 3 * n, 2 * w), add (v + 2 * n, u + 3 * n, 2 * w);
	}
	dij();
	for (int i = 2; i <= n; i++)
		printf ("%lld ", min(dis[i], dis[i + 3 * n]));
	return 0;
}
posted @ 2025-10-11 16:14  williamYcY  阅读(3)  评论(0)    收藏  举报
/* */ /* */ /* */ /* */ /* */ /* */