codeforces round 790

传送门

790

A

拆分开,前三位 和 后三位依次相加

B

让每个盒子里所剩糖果数为所有盒子糖果数的 min 即可

C

由于数据范围较小,所以暴力

由于 a 只能到 b , z 只能到 y , 所以最少步数就是 两个字母差值的绝对值

void solve()
{
    int n, m; cin >> n >> m;
    int mi = 0x3f3f3f3f;
    vector<string> s(n);
    for(int i = 0; i < n; i++) cin >> s[i];
    for(int i = 0; i < n - 1; i++) {
    	for(int j = i + 1; j < n; j++) {
    		int cnt = 0;
    		for(int k = 0; k < m; k++) cnt += abs(s[i][k] - s[j][k]);
    		mi = min(mi, cnt);
    	}
    }
    cout << mi << '\n';
}

D

(傻人办法)

在此处通过对角线延申 , 但此处也可以是边框部分

一个一个计算对角线很复杂,想着把所有对角线的和记录下来

  • 左对角线下标之和相等
  • 右对角线差值相等 (不想求和时出现负下标,可以加上 \(max(n, m)\)

之后再遍历一遍网格,取最大的和即可

求和时记得减去一个自身

void solve()
{
    int n, m; cin >> n >> m;
    int u = max(n, m);
    vector<ll> left(100000), right(100000);
    vector<vector<int>> a(n + 1, vector<int>(m + 1));
    ll ma = 0;
    for(int i = 1; i <= n; i++) {
    	for(int j = 1; j <= m; j++) cin >> a[i][j];
    }
    for(int i = 1; i <= n; i++) {
    	int q = i, w = 1;
    	while(q <= n && w <= m) right[i - 1 + u] += a[q][w], q++, w++;
    }
    for(int i = 2; i <= m; i++) {
    	int q = 1, w = i;
    	while(q <= n && w <= m) right[1 - i + u] += a[q][w], q++, w++;
    }
    for(int i = 1; i <= m; i++) {
    	int q = 1, w = i;
    	while(q <= n && w >= 1) left[1 + i] += a[q][w], q++, w--;
    }
    for(int i = 2; i <= n; i++) {
    	int q = i, w = m;
    	while(q <= n && w >= 1) left[i + m] += a[q][w], q++, w--;
    }
    for(int i = 1; i <= n; i++) {
    	for(int j = 1; j <= m; j++) {
    		ma = max(ma, left[i + j] + right[i - j + u] - a[i][j]);
    	}
    }
    cout << ma << '\n';
}

E

思路很简单,因为 大于等于 即可,就往大了选,从后往前,就是最少糖果数

注意数据范围很大,要使用 后缀和二分

void solve()
{
    int n, q; cin >> n >> q;
    vector<int> a(n);
    vector<ll> suf(n + 1);
    ll su = 0;
    for(int i = 0; i < n; i++) {cin >> a[i]; su += a[i];}
    sort(a.begin(), a.end());
    for(int i = n - 1; i >= 0; i--) suf[n - i] = suf[n - (i + 1)] + a[i];
    while(q--) {
    	int u, ed = 0, cnt = 0; cin >> u;
    	ll sum = 0;
    	if(u > su) {cout << -1 << '\n'; continue;}
    	auto q = lower_bound(suf.begin(), suf.end(), u) - suf.begin();
    	cout << q << '\n';
    }
}

F

本质是 求数组中连续子序列的最大长度 , 加了个条件,这个数组中的数必须出现 \(k\) 次以上

连续子序列的最大长度

  • 依次遍历数组中的数(去重后)

  • \(x - 1\) 存在,则说明 \(x\) 不应该作为起始点,跳过

  • 反之,继续判断 \(x + 1\) 是否存在,存在则 \(cnt++\) ,接着继续判断,直到有一个数不存在为止,这一段就结束了

void solve()
{
    int n, k, go = 0; cin >> n >> k;
    vector<int> a(n);
    map<int, int> mp, st;
    ll ma = 0, l = 0, r = 0;
    for (int i = 0; i < n; i++) {cin >> a[i]; mp[a[i]]++;}
    for (auto& [q, w] : mp) {
        if (w >= k) st[q] = 1, go = 1;
    }
    if (go == 0) { cout << -1 << '\n'; return; }
    for (auto& [u, q] : st) {
        if (st[u - 1]) continue;
        int y = u;
        ll cnt = 0;
        while (st[y]) y++, cnt++;
        if(cnt > ma) l = u, r = y - 1, ma = cnt;
    }
    cout << l << ' ' << r << '\n';
}

G

dfs求有多少个符合条件的点

这个点为 \(W\), 贡献则为 \(-1\); 为 \(B\) , 贡献则为 \(1\)

当此处 \(cnt[x]\)\(0\) 时,则符合条件

void solve()
{
    int n, cn = 0; cin >> n;
    vector<int> g[5000], cnt(n + 10);
    for(int i = 2; i <= n; i++) {
    	int u; cin >> u;
    	g[u].push_back(i);
    }
    string s; cin >> s;
    function<void(int)> dfs = [&] (int u) {
    	cnt[u] = (s[u - 1] == 'W' ? -1 : 1);
    	for(auto& q : g[u]) {
    		dfs(q);
    		cnt[u] += cnt[q];
    	}
    	if(cnt[u] == 0) cn++;
    };
    cn = 0, dfs(1);
    cout << cn << '\n';
}

H

交点 : 只有 前面的位置大于后面的位置 才会出现,则是 逆序对

但是, 位置相同,在同一段区间,我们任意安排,就能多出交点。所以在 \(query\) 时,只需要查询小于等于 \(x - 1\) 的数有多少个即可

树状数组求逆序对

ll tr[N];

int lowbit(int x) {return x & (-x);}

void add(int x, ll u) {
	for(int i = x; i <= n; i += lowbit(i)) tr[i] += u;
}

ll query(int x) {
	ll ans = 0;
	while(x) {
		ans += tr[x];
		x -= lowbit(x);
	}
	return ans;
}

void solve()
{
    cin >> n;
    ll cnt = 0;
    memset(tr, 0, sizeof tr);
    for(int i = 0; i < n; i++) {
    	ll u; cin >> u;
    	cnt += i - query(u - 1);
    	add(u, 1);
    }
    cout << cnt << '\n';
}
posted @ 2025-07-05 02:48  PeachyGalaxy  阅读(8)  评论(0)    收藏  举报