A 小红的数组重排
题目保证三个元素互不相同,直接位移一位即可。
void solve() {
int a, b, c;
cin >> a >> b >> c;
cout << c << " " << a << " " << b << endl;
}
B 小红的回文串构造
显然的,除了长度为 $1$ 的回文串,其他回文串必然有相同字符。
void solve() {
int n;
cin >> n;
if (n == 1)
cout << 'a' << endl;
else
cout << "No" << endl;
}
C 小红的排列
最终一定是奇数有 $A=\lceil\frac{n}{2}\rceil$ 个,偶数有 $B=\lfloor\frac{n}{2}\rfloor$ 个,边界条件就是:必须是奇数的数量大于 $A$ ,或者必须是偶数的数量大于 $B$ 。
记有 $x$ 个 $?$ ,$b$ 个偶数,最后的方案数即为
$$
{\text{x个?中选出}B-b\text{个分给偶数的方案数}}\times{\text{奇数全排列}}\times{{\text{偶数全排列}}}\
ans = \binom{x}{\frac{n}{2}-b}\times A!\times B!
$$
void solve() {
int n;
string s;
cin >> n >> s;
Z ans = 1;
int a = 0, b = 0, x = 0;
for (auto v : s) {
a += v == 'j';
b += v == 'o';
x += v == '?';
}
if (b > n / 2 || a > (n + 1) / 2) {
cout << 0 << endl;
return;
}
cout << C.C(x, n / 2 - b) * C.fac(n / 2) * C.fac((n + 1) / 2) << endl;
}
D 小红的中位数
排序后,记中位数为 $x$ ,数组为 ${a_1,a_2,…,x,x,x,…,a_{n-1},a_n}$ ,记录中位数左边和右边的数字个数 $L$ 和 $R$ 。
若新中位数要小于 $x$ ,那么剩余数组里至少有一半以上的位置要被小于 $x$ 的数占住,因此剩余长度不能超过 $2L$ 。
若新中位数要大于 $x$ ,那么剩余数组里必须有足够多的大于 $x$ 的数把中位数顶过去,因此剩余长度不能超过 $2R-1$。
直接计算即可。
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) cin >> a[i];
sort(all(a));
int L = lower_bound(all(a), a[(n - 1) / 2]) - a.begin();
int R = a.end() - upper_bound(all(a), a[(n - 1) / 2]);
if (L == 0 && R == 0) {
cout << -1 << endl;
return;
}
int ans = n + 1;
if (L > 0) ans = min(ans, n - 2 * L);
if (R > 0) ans = min(ans, n - (2 * R - 1));
cout << ans << endl;
}
E 小红的树上博弈
我们用 $f(x)$ 记录小红走到这个点能否保证一定胜利。
叶子节点一定是 $f(x) = true$,因为小红一旦走到叶子就立刻获胜。
对于非根节点 $u$ ,如果它的孩子里,至少有 $2$ 个是 $f(x)=true$ ,那么小红必胜。因为紫方每回合只能封掉 $u$ 的一个相邻红点,红方总能选另一个仍然能继续获胜的孩子。
对于根节点 $1$ ,因为一开始红方先走、紫方还没来得及封点,所以根节点只要有至少 $1$ 个 $f(x)=true$ 的孩子,红方就能赢。
递归版:
void solve() {
int n;
cin >> n;
vector<vector<int>> g(n + 1);
for (int i = 1; i < n; ++i) {
int u, v;
cin >> u >> v;
g[u].pb(v), g[v].pb(u);
}
vector<bool> f(n + 1);
auto dfs = [&](auto &&self, int u, int fa) -> void {
int cnt = 0;
bool ok = 1;
for (auto v : g[u]) {
if (v == fa) continue;
ok = 0;
self(self, v, u);
cnt += f[v];
}
if (ok)
f[u] = 1;
else if (u == 1)
f[u] = cnt >= 1;
else
f[u] = cnt >= 2;
};
dfs(dfs, 1, 0);
cout << (f[1] ? "red" : "purple") << endl;
}
部分题目可能会出现递归过深导致爆栈的问题,在懒得开大占空间的情况下,我更推荐您提前练习将此类题目写成显式的版本。
void solve() {
int n;
cin >> n;
vector<vector<int>> g(n + 1);
for (int i = 1; i < n; ++i) {
int u, v;
cin >> u >> v;
g[u].pb(v), g[v].pb(u);
}
vector<int> pa(n + 1), order, st;
st.pb(1);
pa[1] = 0;
while (!st.empty()) {
auto u = st.back();
st.pob();
order.pb(u);
for (auto v : g[u]) {
if (v == pa[u]) continue;
pa[v] = u;
st.pb(v);
}
}
vector<bool> f(n + 1);
for (int i = n - 1; i >= 0; --i) {
int u = order[i], cnt = 0;
for (auto v : g[u]) {
if (pa[v] == u && f[v]) cnt++;
}
bool ok = true;
for (auto v : g[u]) {
if (pa[v] == u) {
ok = false;
break;
}
}
if (ok)
f[u] = 1;
else if (u == 1)
f[u] = cnt >= 1;
else
f[u] = cnt >= 2;
}
cout << (f[1] ? "red" : "purple") << endl;
}
F 小红的点构造
先计算出所有 $n$ 个点最多能构造多少对相邻点,然后找在这样的构造方案下, $k$ 组相邻需要多少个点,剩余的边用一条链接在初始点上,多余的点隔一格放在角落即可。
void solve() {
int n, k;
cin >> n >> k;
vector<PII> P;
vector<ll> E;
P.pb({0, 0});
E.pb(0);
int d = 1;
while (P.size() < n) {
for (int x = 0; x < d && P.size() < n; ++x) {
P.pb({x, d});
E.pb(E.back() + (x == 0 ? 1 : 2));
}
for (int y = 0; y < d && P.size() < n; ++y) {
P.pb({d, y});
E.pb(E.back() + (y == 0 ? 1 : 2));
}
if (P.size() < n) {
P.pb({d, d});
E.pb(E.back() + 2);
}
d++;
}
if (E.back() < k) {
cout << "No" << endl;
return;
}
cout << "Yes" << endl;
int pos = 0;
for (int i = 0; i < n; ++i) {
if (E[i] <= k) {
pos = i + 1;
}
}
for (int i = 0; i < pos; ++i) {
cout << P[i].fi << " " << P[i].se << endl;
}
int r = k - E[pos - 1], rem = n - pos, ptr = -1;
if (rem) {
if (r == 1) {
int mx = -1, yy = -1;
for (int i = 0; i < pos; ++i) {
if (P[i].fi > mx) {
mx = P[i].fi;
yy = P[i].se;
}
}
cout << mx + 1 << " " << yy << endl;
rem--;
}
int x = -1e8, y = -1e8;
while (rem > 0) {
cout << x << " " << y << endl;
x += 2;
rem--;
}
}
}
头文件
#include <bits/stdc++.h>
#define pb push_back
#define pf push_front
#define pob pop_back
#define pof pop_front
#define eb emplace_back
#define fi first
#define se second
#define all(a) a.begin(), a.end()
#define rall(a) a.rbegin(), a.rend()
#define endl "\n"
using namespace std;
using ll = long long;
using ld = long double;
using ui = unsigned;
using ull = unsigned long long;
using i128 = __int128;
using PII = pair<int, int>;
using PLL = pair<ll, ll>;
using TIII = tuple<int, int, int>;
using TLLL = tuple<ll, ll, ll>;
void init() {
}
void solve() {
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
init();
int t = 1;
cin >> t;
cout << fixed << setprecision(15);
for (int _ = 1; _ <= t; ++_) {
solve();
}
return 0;
}
自动取模
constexpr int MOD = 998244353;
template <class T>
constexpr T ksm(T a, ll b) {
T res = 1;
while (b) {
if (b & 1)
res *= a;
b >>= 1;
a *= a;
}
return res;
}
template <int P>
struct MInt {
int x;
constexpr MInt() : x() {}
constexpr MInt(ll x) : x{norm(x % getMod())} {}
static int Mod;
constexpr static int getMod() {
if (P > 0) {
return P;
} else {
return Mod;
}
}
constexpr static void setMod(int Mod_) {
Mod = Mod_;
}
constexpr int norm(int x) const {
if (x < 0) {
x += getMod();
}
if (x >= getMod()) {
x -= getMod();
}
return x;
}
constexpr int val() const {
return x;
}
explicit constexpr operator int() const {
return x;
}
constexpr MInt operator-() const {
MInt res;
res.x = norm(getMod() - x);
return res;
}
constexpr MInt inv() const {
assert(x != 0);
return ksm(*this, getMod() - 2);
}
constexpr MInt &operator*=(MInt rhs) & {
x = 1LL * x * rhs.x % getMod();
return *this;
}
constexpr MInt &operator+=(MInt rhs) & {
x = norm(x + rhs.x);
return *this;
}
constexpr MInt &operator-=(MInt rhs) & {
x = norm(x - rhs.x);
return *this;
}
constexpr MInt &operator/=(MInt rhs) & {
return *this *= rhs.inv();
}
friend constexpr MInt operator*(MInt lhs, MInt rhs) {
MInt res = lhs;
res *= rhs;
return res;
}
friend constexpr MInt operator+(MInt lhs, MInt rhs) {
MInt res = lhs;
res += rhs;
return res;
}
friend constexpr MInt operator-(MInt lhs, MInt rhs) {
MInt res = lhs;
res -= rhs;
return res;
}
friend constexpr MInt operator/(MInt lhs, MInt rhs) {
MInt res = lhs;
res /= rhs;
return res;
}
friend constexpr istream &operator>>(istream &is, MInt &a) {
ll v;
is >> v;
a = MInt(v);
return is;
}
friend constexpr ostream &operator<<(ostream &os, const MInt &a) {
return os << a.val();
}
friend constexpr bool operator==(MInt lhs, MInt rhs) {
return lhs.val() == rhs.val();
}
friend constexpr bool operator!=(MInt lhs, MInt rhs) {
return lhs.val() != rhs.val();
}
};
template <>
int MInt<0>::Mod = 998244353;
template <int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();
using Z = MInt<MOD>;
组合数学
struct Comb {
int n;
vector<Z> _fac, _inv;
Comb() : n(0), _fac{1}, _inv{0} {}
Comb(int n) : Comb() {
init(n);
}
void init(int m) {
if (m <= n)
return;
_fac.resize(m + 1);
_inv.resize(m + 1);
for (int i = n + 1; i <= m; i++) {
_fac[i] = _fac[i - 1] * i;
}
_inv[m] = _fac[m].inv();
for (int i = m; i > n; i--) {
_inv[i - 1] = _inv[i] * i;
}
n = m;
}
Z fac(int x) {
if (x > n)
init(x);
return _fac[x];
}
Z inv(int x) {
if (x > n)
init(x);
return _inv[x];
}
Z C(int x, int y) {
if (x < 0 || y < 0 || x < y)
return 0;
return fac(x) * inv(y) * inv(x - y);
}
Z A(int x, int y) {
if (x < 0 || y < 0 || x < y)
return 0;
return fac(x) * inv(x - y);
}
};