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


A. Find Divisible

点击查看代码
void solve() {
    int l, r;
    std::cin >> l >> r;
    std::cout << l << " " << l * 2 << "\n";
}

B. Substring Removal

题意:给你一个字符串,删去恰好一个子串,使得剩下字符全部相等。求方案数。

分类讨论,记\(cnt_l\)为最大相等的前缀的长度,\(cnt_r\)为最大相等后缀的长度。
如果\(cntl = n\),也就是所有字符都相等,那么方案数就是任选两个位置:\(\frac{n(n+1)}{2}\)
否则如果\(s_1 = s_n\),意味着前缀的字符和后缀的相等。那么只需要把中间的都删掉就行。方案数为\((cnt_l + 1) \times (cnt_r + 1)\),否则我们需要只保留前缀一部分,或者只保留后缀一部分,答案是\(cnt_l + cnt_r + 1\)

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
    const int mod = 998244353;
    int cntl = 0, cntr = 0;
    for (int i = 0; i < n; ++ i) {
    	if (s[i] == s[0]) {
    		++ cntl;
    	} else {
    		break;
    	}
    }

    for (int i = n - 1; i >= 0; -- i) {
    	if (s[i] == s[n - 1]) {
    		++ cntr;
    	} else {
    		break;
    	}
    }

    if (cntl == n) {
	    int ans = (i64)n * (n + 1) / 2 % mod;
	    std::cout << ans << "\n";
    } else {
    	if (s[0] == s[n - 1]) {
    		int ans = (i64)(cntl + 1) * (cntr + 1) % mod;
    		std::cout << ans << "\n";
    	} else {
    		int ans = cntl + cntr + 1;
    		std::cout << ans << "\n";
    	}
    }
}

C. Polygon for the Angle

题意:给你一个角度\(a\),求一个最小的\(n\),使得\(n\)正边形可以选出三个点,使得它们构成的角度是\(a\)

\(n\)正边形最小角度为\(\frac{180}{n}\),这三个点每扩大一个点的距离,角度会增大\(\frac{180}{n}\)。所以我们可以枚举\(n\),找到他能表示的所有角度。注意\(n=360\)可以表示所有度数,所以只需要枚举到\(360\)

点击查看代码
int ans[361];

void init() {
	for (int i = 3; i <= 360; ++ i) {
		for (int j = 1; j <= i - 2; ++ j) {
			if (180 * j % i == 0 && ans[180 * j / i] == 0) {
				ans[180 * j / i] = i;
			}
		}
	}
}

void solve() {
    int n;
    std::cin >> n;
    std::cout << ans[n] << "\n";
}


int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int T = 1; 
	init();
	std::cin >> T;
	//scanf("%d", &T);
	while (T -- ) {
		solve();
	} 
	return 0;
}

D. Easy Problem

题意:给你一个字符串,每个位置价值为\(a_i\)。你要删除一些字符,使得字符串没有"hard"这个子序列。

\(f[i][0/1/2/3]\)表示\([1, i]\)没有"h"这个子序列、没有"ha"这个子序列、没有"har"这个子序列、没有"hard"这个子序列的最小值。

那么如果\(s_i\) = 'h',则如果不删,就会出现"h",则\(f[i][1] = \min(f[i][1], f[i - 1][1])\),如果删,则\(f[i][0] = f[i - 1][0] + a_i\)
其它位置同理考虑:如果\(s_i\) = 'a',\(f[i][2] = \min(f[i][2], f[i - 2][2]), f[i][1] = f[i - 1][1] + a_i\)
如果\(s_i\) = 'r',\(f[i][3] = \min(f[i][3], f[i - 2][3]), f[i][2] = f[i - 1][2] + a_i\)
如果\(s_i\) = 'd',\(f[i][3] = f[i - 1][3] + a_i\)

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }	

    std::array<i64, 4> f{};
    f[0] = 0;
    for (int i = 0; i < n; ++ i) {
    	auto g = f;
	    if (s[i] == 'h') {
	    	g[1] = std::min(g[1], g[0]);
	    	g[0] += a[i];
	    } else if (s[i] == 'a') {
	    	g[2] = std::min(g[2], g[1]);
	    	g[1] += a[i];
	    } else if (s[i] == 'r') {
	    	g[3] = std::min(g[3], g[2]);
	    	g[2] += a[i];
	    } else if (s[i] == 'd') {
	    	g[3] += a[i];
	    }

	    f = g;
    } 

    std::cout << std::min({f[0], f[1], f[2], f[3]}) << "\n";
}

E. The Top Scorer

待补。


F. Inversion Expectation

题意:一个排列,有些地方是\(-1\)代表没填。求逆序对的期望。

设有\(cnt\)\(-1\)。比\(i\)小的数里未出现的有\(l_i\)个,比它大的未出现的有\(r_i\)个。
分类讨论:
一、已填数和已填数之间的逆序对
这个用树状数组或归并排序计算即可。
二、未填数和未填数之间的逆序对
任选两个未填数,它们为逆序对的几率为\(\frac{1}{2}\),总共有\(\frac{cnt(cnt-1)}{2}\)对,则期望为\(\frac{cnt(cnt-1)}{4}\)
三、已填数和它右边的未填数构成的逆序对
右边比它小的概率是\(\frac{l_i}{cnt}\),如果右边总共有\(suf\)\(-1\),则期望为\(\frac{l_i\times suf}{cnt}\)
四、已填数和它左边的未填数构成的逆序对
左边比它大的概率是\(\frac{r_i}{cnt}\),如果左边总共有\(pre\)\(-1\),则期望为\(\frac{r_i \times pre}{cnt}\)

\(l_i, r_i\)可以预处理。

点击查看代码
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);
    }
};

const int mod = 998244353;

int power(int a, int b) {
	int res = 1;
	for (; b ; b >>= 1, a = 1LL * a * a % mod) {
		if (b & 1) {
			res = 1LL * res * a % mod;
		}
	}

	return res;
}

int inv(int a) {
	return power(a, mod - 2);
}

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n), st(n + 1);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    	if (a[i] != -1) {
    		st[a[i]] = 1;
    	}
    }

    int cnt = std::ranges::count(a, -1);
    std::vector<int> l(n + 1), r(n + 1);
    for (int i = 1; i <= n; ++ i) {
    	l[i] = l[i - 1] + !st[i];
    	r[i] = cnt - l[i];
    }

    int ans = 1LL * cnt * (cnt - 1) % mod * inv(4) % mod;
    int pre = 0;
    for (int i = 0; i < n; ++ i) {
    	if (a[i] != -1) {
    		ans = (ans + 1LL * pre % mod * r[a[i]] % mod * inv(cnt) % mod) % mod;
    	} else {
    		pre += 1;
    	}
    }

    int suf = 0;
    for (int i = n - 1; i >= 0; -- i) {
    	if (a[i] != -1) {
    		ans = (ans + 1LL * suf % mod * l[a[i]] % mod * inv(cnt) % mod) % mod;
    	} else {
    		suf += 1;
    	}
    }

    Fenwick<int> tr(n + 1);
    for (int i = 0; i < n; ++ i) {
    	if (a[i] != -1) {
    		ans = (ans + tr.sum(a[i], n)) % mod;
	    	tr.add(a[i], 1);
    	} 
    }

    std::cout << ans << "\n";
}
posted @ 2025-04-11 16:38  maburb  阅读(10)  评论(0)    收藏  举报