T1. 逆序对数
对于任何子序列 \(S\),自然有 \(inv(S) \leqslant inv(p)\),取等当且仅当对于任何 \(i < j, a_i > a_j, i, j\) 都在 \(S\) 中。那么如果存在 \(j < i\) 使得 \(a_j > a_i\) 或是存在 \(j > i\) 使得 \(a_j < a_i\),那么 \(i\) 就必须在 \(S\) 中,预处理前后缀最值即可。设不必须在 \(S\) 中的元素有 \(k\) 个,那么答案就是 \(2^k\),特别地,当 \(k = n\) 时答案是 \(2^n-1\),因为要排除空子序列。
时间复杂度为 \(O(n)\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
const int mod = 998244353;
//const int mod = 1000000007;
struct mint {
ll x;
mint(ll x=0):x((x%mod+mod)%mod) {}
mint operator-() const {
return mint(-x);
}
mint& operator+=(const mint a) {
if ((x += a.x) >= mod) x -= mod;
return *this;
}
mint& operator-=(const mint a) {
if ((x += mod-a.x) >= mod) x -= mod;
return *this;
}
mint& operator*=(const mint a) {
(x *= a.x) %= mod;
return *this;
}
mint operator+(const mint a) const {
return mint(*this) += a;
}
mint operator-(const mint a) const {
return mint(*this) -= a;
}
mint operator*(const mint a) const {
return mint(*this) *= a;
}
mint pow(ll t) const {
if (!t) return 1;
mint a = pow(t>>1);
a *= a;
if (t&1) a *= *this;
return a;
}
// for prime mod
mint inv() const {
return pow(mod-2);
}
mint& operator/=(const mint a) {
return *this *= a.inv();
}
mint operator/(const mint a) const {
return mint(*this) /= a;
}
};
istream& operator>>(istream& is, mint& a) {
return is >> a.x;
}
ostream& operator<<(ostream& os, const mint& a) {
return os << a.x;
}
void solve() {
int n;
cin >> n;
vector<int> p(n);
rep(i, n) cin >> p[i];
vector<int> prefmax(n), sufmin(n);
int mx = 0, mn = n+1;
rep(i, n) {
mx = max(mx, p[i]);
if (p[i] == mx) prefmax[i] = 1;
mn = min(mn, p[n-1-i]);
if (p[n-1-i] == mn) sufmin[n-1-i] = 1;
}
int cnt = 0;
rep(i, n) cnt += prefmax[i]*sufmin[i];
mint ans = mint(2).pow(cnt);
if (cnt == n) ans -= 1;
cout << ans << '\n';
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
T2. 平衡 01 串
二分答案转为判定答案能否 \(\leqslant mid\),对于每个 \(l\),应取最大的 \(r\) 使得区间内的 \(0\) 的个数尽可能多并且不超过 \(mid\),然后当区间外的 \(1\) 的个数也不超过 \(mid\) 时即可行,于是可以做到 \(O(|S|\log |S|)\)。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
void solve() {
string s;
cin >> s;
int n = s.size();
vector<int> sum(n+1);
rep(i, n) sum[i+1] = sum[i] + (s[i] == '0');
vector<int> is;
rep(i, n) if (s[i] == '1') is.push_back(i);
int m = is.size();
int ac = m, wa = -1;
while (abs(ac-wa) > 1) {
int wj = (ac+wa)/2;
bool ok = false;
rep(i, wj+1) {
int l = is[i], r = is[m-1-wj+i]+1;
if (sum[r] - sum[l] <= wj) {
ok = true;
break;
}
}
(ok ? ac : wa) = wj;
}
cout << ac << '\n';
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
T3. 城市漫步
以 \(x\) 为根,如果 \(x=y\),那么相当于最终每个关键点到根的路径上的边都会被经过两次(往返),按任意 \(\operatorname{dfs}\) 序遍历即可达到此下界,这也说明访问关键点的顺序可以任意,并且不影响达到下界。接下来考虑 \(y \neq x\) 时,到达最后一个关键点时要从返回 \(x\) 变成走向 \(y\),那么把 \(y\) 也视作关键点统计下界,最后走向 \(y\) 可不用返回,再减去 \(x\) 到 \(y\) 的距离即可。
时间复杂度为 \(O(n)\)。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
void solve() {
int n, k, x, y;
cin >> n >> k >> x >> y;
--x; --y;
vector<int> a(k);
rep(i, k) cin >> a[i], a[i]--;
a.push_back(y);
vector<vector<int>> to(n);
rep(i, n-1) {
int u, v;
cin >> u >> v;
--u; --v;
to[u].push_back(v);
to[v].push_back(u);
}
vector<int> p(n, -1), dep(n);
auto dfs = [&](auto& f, int v) -> void {
for (int u : to[v]) {
if (u == p[v]) continue;
p[u] = v;
dep[u] = dep[v]+1;
f(f, u);
}
};
dfs(dfs, x);
int ans = 0;
vector<bool> used(n);
used[x] = true;
for (int v : a) {
while (!used[v]) {
used[v] = true;
ans += 2;
v = p[v];
}
}
ans -= dep[y];
cout << ans << '\n';
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
T4. 随机游走
令 \(P(i, j)\) 表示经过 \(j-i\) 次操作后 \(l=i\) 且 \(r=j\) 的概率,显然 \(P(i, i) = \frac{1}{n}\),\(P(1, n) = 1\),接下来只考虑长度至少为 \(2\) 且小于 \(n\) 的区间。
在 \(i > 1\) 且 \(j < n\) 时显然有 \(P(i, j) = \frac{1}{2}P(i, j-1) + \frac{1}{2}P(i+1, j)\),简单归纳可得 \(P(i, j) = \frac{1}{n}(1 < i \leqslant j < n)\)。
对于 \(i = 1\) 且 \(j < n\) 的情况,有 \(P(1, j) = P(1, j-1) + \frac{1}{2}P(2, j) = P(1, j-1) + \frac{1}{2n}\),简单归纳可得 \(P(1, j) = \frac{j+1}{2n}(j < n)\)。
同理有 \(P(i, n) = P(i+1, n) + \frac{1}{2}P(i, n-1) = P(i+1, n) + \frac{1}{2n}\),简单归纳可得 \(P(i, n) = \frac{n-i+2}{2n}(i > 1)\)。
如果某个位置 \(i\) 是在区间长度为 \(j\) 时被扩展到的,那么 \(sum\) 会加上 \(j \times a_i\),那么其实可以看做是这 \(j\) 次扩展时每次都加了 \(a_i\)。由于每个位置最终都会被扩展到,因此 \(sum\) 必定会加上 \(a_i\),而剩下的 \((j-1) \times a_i\) 便是前 \(j-1\) 次扩展的贡献,而前 \(j-1\) 次扩展的共同点是它们都不包含 \(i\) 这个位置,也就是说 \(i\) 这个位置的贡献可以表示为
\( \begin{aligned} &\left(1+\sum_{i \notin [l, r]} P(l, r)\right) \times a_i\\ =& \left(1 + \sum_{l \leqslant r < i} P(l, r) + \sum_{i < l \leqslant r} P(l, r)\right) \times a_i\\ =& \left(1 + \sum_{r < i} P(1, r) + \sum_{1 < l \leqslant r < i} P(l, r) + \sum_{i < l} P(l, n) + \sum_{i < l \leqslant r < n} P(l, r)\right) \times a_i\\ =& \left(1 + \frac{\frac{i(i+1)}{2}-1}{2n} + \frac{\frac{(i-1)(i-2)}{2}}{n} + \frac{\frac{(n-i+1)(n-i+2)}{2}-1}{2n} + \frac{\frac{(n-i)(n-i-1)}{2}}{n}\right) \times a_i \end{aligned} \)
于是可以在 \(O(n)\) 时间内得到答案。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
const int mod = 666528221;
// const int mod = 998244353;
//const int mod = 1000000007;
struct mint {
ll x;
mint(ll x=0):x((x%mod+mod)%mod) {}
mint operator-() const {
return mint(-x);
}
mint& operator+=(const mint a) {
if ((x += a.x) >= mod) x -= mod;
return *this;
}
mint& operator-=(const mint a) {
if ((x += mod-a.x) >= mod) x -= mod;
return *this;
}
mint& operator*=(const mint a) {
(x *= a.x) %= mod;
return *this;
}
mint operator+(const mint a) const {
return mint(*this) += a;
}
mint operator-(const mint a) const {
return mint(*this) -= a;
}
mint operator*(const mint a) const {
return mint(*this) *= a;
}
mint pow(ll t) const {
if (!t) return 1;
mint a = pow(t>>1);
a *= a;
if (t&1) a *= *this;
return a;
}
// for prime mod
mint inv() const {
return pow(mod-2);
}
mint& operator/=(const mint a) {
return *this *= a.inv();
}
mint operator/(const mint a) const {
return mint(*this) /= a;
}
};
istream& operator>>(istream& is, mint& a) {
return is >> a.x;
}
ostream& operator<<(ostream& os, const mint& a) {
return os << a.x;
}
ll c2(ll n) {
return n*(n-1)/2;
}
int main() {
int n;
cin >> n;
vector<int> a(n);
rep(i, n) cin >> a[i];
mint ans;
rep(i, n) {
mint coef = 1;
coef += mint(c2(i+2)-1)/(2*n);
coef += mint(c2(i))/n;
coef += mint(c2(n-i+1)-1)/(2*n);
coef += mint(c2(n-i-1))/n;
ans += coef*a[i];
}
cout << ans << '\n';
return 0;
}
浙公网安备 33010602011771号