Limited Insertion
考虑最后一次操作,一定是当前序列中最后一个合法的位置(删前面会导致这个位置以后都不合法),模拟即可。
#include <bits/stdc++.h>
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
--a[i];
}
vector<int> ans;
for (int i = 0; i < n; ++i) {
int p = -1;
for (int j = 0; j < n - i; ++j) {
if (a[j] == j) {
p = j;
}
}
if (p == -1) {
cout << -1 << "\n";
return 0;
}
ans.push_back(p);
a.erase(a.begin() + p);
}
reverse(ans.begin(), ans.end());
for (int i = 0; i < n; ++i) {
cout << ans[i] + 1 << "\n";
}
return 0;
}
Balanced Neighbors
对于 n n n 是奇数的情况,考虑完全图去掉 i + j = n i+j=n i+j=n 的边;对于 n n n 是偶数的情况,考虑完全图去掉 i + j = n + 1 i+j=n+1 i+j=n+1 的边即可。
#include <bits/stdc++.h>
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<pair<int, int>> ans;
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j) {
if (i + j != n + (n % 2 == 0)) {
ans.emplace_back(i, j);
}
}
}
cout << ans.size() << "\n";
for (auto p : ans) {
cout << p.first << " " << p.second << "\n";
}
return 0;
}
Three Circuits
首先判断图是否有欧拉回路。如果有至少一个度数大于等于 6 6 6 的点,可以把它在欧拉回路中出现的位置拆成三个环;如果有至少三个度数为 4 4 4 的点,也可以讨论一下拆成三个环;如果有两个度数为 4 4 4 的点,分为两种情况讨论,有自环的有解,没有自环的无解。其他情况均无解。
#include <bits/stdc++.h>
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
vector<vector<int>> adj(n);
vector<int> deg(n);
for (int i = 0; i < m; ++i) {
int from, to;
cin >> from >> to;
--from;
--to;
++deg[from];
++deg[to];
adj[from].push_back(to);
adj[to].push_back(from);
}
for (int i = 0; i < n; ++i) {
if (deg[i] & 1) {
cout << "No" << "\n";
return 0;
}
}
for (int i = 0; i < n; ++i) {
if (deg[i] >= 6) {
cout << "Yes" << "\n";
return 0;
}
}
int cnt = 0;
for (int i = 0; i < n; ++i) {
if (deg[i] == 4) {
++cnt;
}
}
if (cnt >= 3) {
cout << "Yes" << "\n";
return 0;
}
if (cnt == 2) {
for (int i = 0; i < n; ++i) {
if (deg[i] == 2) {
int x = adj[i][0], y = adj[i][1];
if (x == y) {
cout << "Yes" << "\n";
return 0;
}
for (int j = 0; j < (int) adj[x].size(); ++j) {
if (adj[x][j] == i) {
adj[x][j] = y;
break;
}
}
for (int j = 0; j < (int) adj[y].size(); ++j) {
if (adj[y][j] == i) {
adj[y][j] = x;
break;
}
}
}
}
}
cout << "No" << "\n";
return 0;
}
Rotation Sort
考虑两个操作实际上是把一个数往左或往右扔到任意位置,从小到大DP保留的数,别的数扔的方向是固定的。
#include <bits/stdc++.h>
using namespace std;
const long long inf = 1ll << 60;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, a, b;
cin >> n >> a >> b;
vector<int> p(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> p[i];
}
vector<int> q(n + 1);
for (int i = 1; i <= n; ++i) {
q[p[i]] = i;
}
vector<long long> dp(n + 1, inf);
dp[0] = 0;
for (int i = 1; i <= n; ++i) {
vector<long long> new_dp(n + 1, inf);
for (int j = 0; j < i; ++j) {
if (dp[j] != inf) {
if (q[i] > q[j]) {
new_dp[i] = min(new_dp[i], dp[j]);
new_dp[j] = min(new_dp[j], dp[j] + b);
} else {
new_dp[j] = min(new_dp[j], dp[j] + a);
}
}
}
swap(dp, new_dp);
}
cout << *min_element(dp.begin(), dp.end()) << "\n";
return 0;
}
Modulo Pairing
可以用调整法证明:存在一个分界点,前面是 x + y < M x+y<M x+y<M 的匹配,后面是 x + y ≥ M x+y\ge M x+y≥M 的匹配,且两边的匹配一定是第一个匹配最后一个,第二个匹配倒数第二个,以此类推。二分这个分界点即可。
#include <bits/stdc++.h>
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
vector<int> a(n * 2);
for (int i = 0; i < n * 2; ++i) {
cin >> a[i];
}
sort(a.begin(), a.end());
int l = 0, r = n;
while (l < r) {
int mid = (l + r + 1) >> 1;
bool red = true;
for (int i = 0; i < mid; ++i) {
if (a[n * 2 - mid + i] + a[n * 2 - mid - i - 1] < m) {
red = false;
break;
}
}
if (red) {
l = mid;
} else {
r = mid - 1;
}
}
int ans = 0;
for (int i = 0; i < l; ++i) {
ans = max(ans, a[n * 2 - l + i] + a[n * 2 - l - i - 1] - m);
}
for (int i = 0; i < n - l; ++i) {
ans = max(ans, a[i] + a[n * 2 - l * 2 - i - 1]);
}
cout << ans << "\n";
return 0;
}
One Third
假设第一刀切在 0 0 0 位置,将其他切的位置分为三种颜色: [ 0 , 1 3 ) [0, \frac{1}{3}) [0,31) 的是红色, [ 1 3 , 2 3 ) [\frac{1}{3}, \frac{2}{3}) [31,32) 的是绿色, [ 2 3 , 1 ) [\frac{2}{3}, 1) [32,1) 的是蓝色。将它们的位置模 1 3 \frac{1}{3} 31 ,则问题等价于:在 0 0 0 处有一个红色点, 1 3 \frac{1}{3} 31 处有一个蓝色点,其他点在 [ 0 , 1 3 ) [0, \frac{1}{3}) [0,31) 内位置和颜色均匀随机,求期望最近异色点对距离。
可以用积分算出长度为 1 1 1 的线段,随机切成 n n n 段,第 k k k 短的期望长度为 1 n 2 + 1 n ( n − 1 ) + ⋯ + 1 n ( n − k + 1 ) \frac{1}{n^2}+\frac{1}{n(n-1)}+\cdots+\frac{1}{n(n-k+1)} n21+n(n−1)1+⋯+n(n−k+1)1 。考虑最短的一段,答案为 1 3 n 2 \frac{1}{3n^2} 3n21 ;如果最短的那一段两个点同色,那么再考虑次短的一段,需要额外加上 1 3 n ( n − 1 ) \frac{1}{3n(n-1)} 3n(n−1)1 ,同时乘上两个点同色的概率 1 3 \frac{1}{3} 31 。以此类推,可以求出答案为 ∑ i = 0 n − 1 1 3 i + 1 n ( n − i ) \sum_{i=0}^{n-1} \frac{1}{3^{i+1}n(n-i)} ∑i=0n−13i+1n(n−i)1 。
#include <bits/stdc++.h>
using namespace std;
const int md = (int) 1e9 + 7;
inline void add(int &x, int y) {
x += y;
if (x >= md) {
x -= md;
}
}
inline int mul(int x, int y) {
return (int) ((long long) x * y % md);
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<int> invs(n + 1);
invs[0] = invs[1] = 1;
for (int i = 2; i <= n; ++i) {
invs[i] = mul(md - md / i, invs[md % i]);
}
int inv_3 = (md + 1) / 3;
int coef = invs[n];
int ans = 0;
for (int i = 0; i < n; ++i) {
coef = mul(coef, inv_3);
add(ans, mul(coef, invs[n - i]));
}
cout << ans << "\n";
return 0;
}