Problem A. Suborrays
笔者的直觉告诉自己,构造一个单峰的排列即可,但没想到任何排列都满足情况。
时间复杂度 (mathcal O(n))
#include <cstdio>
#include <vector>
using namespace std;
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
int cnt = 1;
vector<int> ans;
for (; cnt <= n; cnt += 2) ans.push_back(cnt);
cnt -= 2;
if (cnt + 1 == n)
++cnt;
else
--cnt;
for (; cnt >= 1; cnt -= 2) ans.push_back(cnt);
for (auto i : ans) printf("%d ", i);
puts("");
}
return 0;
}
Problem B. Fix You
因为每一个格子只会有 (R) 或者 (D),那么显然不合法的情况只存在于靠在边界,并且连着外面的情况。
时间复杂度 (mathcal O(nm))
#include <cstdio>
using namespace std;
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, m, ans = 0;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) {
char s[m + 1];
scanf("%s", s);
for (int j = 0; j < m; ++j) {
if (s[j] == 'D' && i == n)
++ans;
if (s[j] == 'R' && j == m - 1)
++ans;
}
}
printf("%d
", ans);
}
return 0;
}
Problem C. Cyclic Permutations
考虑合法方案的构造方法。
排列内部一定存在 ({a,b,c}) 这样一个子序列,满足 (ble a,c) 的情况。
所以答案为 (n!-2^{n-1}),其中 (2^{n-1}) 为所有数放到一边的情况。
时间复杂度 (mathcal O(n))
#include <cstdio>
using namespace std;
typedef long long ll;
const int P = 1e9 + 7;
int main() {
int n;
scanf("%d", &n);
int ans = 1, t = 1;
for (int i = 1; i <= n; ++i) ans = (ll)ans * i % P;
for (int i = 1; i < n; ++i) t = (ll)t * 2 % P;
printf("%d
", (ans - t + P) % P);
return 0;
}
Problem D. 505
显然不可能存在 (4 imes 4) 及以上的子矩阵,因为 (4) 个奇数的和为偶数,那么行数就 (leq 3)。
考虑状压 DP,令 (f[i][j]) 表示前 (i) 列合法,且第 (i) 列的状态为 (j) 的最少修改次数。
[f[i+1][mask]=Min{f[i][j]+fix(mask)}
]
其中 (fix(mask)) 是将原矩阵的状态修改成 (mask) 的修改次数。
时间复杂度 (mathcal O(m imes 2^{2n}))
#include <iostream>
#include <vector>
using namespace std;
template <class T>
void chkmax(T& x, T y) { x = x > y ? x : y; }
template <class T>
void chkmin(T& x, T y) { x = x < y ? x : y; }
int n, m;
vector<string> s;
int calc(int mask, int j) {
int res = 0;
for (int i = 0; i < n; ++i)
if ((mask >> i & 1) != (s[i][j] - '0'))
++res;
return res;
}
bool check(int a, int b) {
for (int i = 0; i < n - 1; ++i)
if (((a >> i & 1) + (a >> i + 1 & 1) + (b >> i & 1) + (b >> i + 1 & 1)) % 2 == 0)
return 0;
return 1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n >> m;
if (n >= 4) {
cout << -1 << '
';
return 0;
}
s.resize(n);
for (int i = 0; i < n; ++i) cin >> s[i];
vector<vector<int>> dp(m, vector<int>(1 << n, 1e9));
for (int mask = 0; mask < 1 << n; ++mask) dp[0][mask] = calc(mask, 0);
for (int i = 0; i < m - 1; ++i)
for (int c_mask = 0; c_mask < 1 << n; ++c_mask)
for (int n_mask = 0; n_mask < 1 << n; ++n_mask)
if (check(c_mask, n_mask))
chkmin(dp[i + 1][n_mask], dp[i][c_mask] + calc(n_mask, i + 1));
int ans = 1e9;
for (int mask = 0; mask < 1 << n; ++mask) chkmin(ans, dp[m - 1][mask]);
cout << ans << '
';
return 0;
}
Problem E. Pairs of Pairs
Task 1 的做法就只需要找到深度最深的一个节点并判断即可。
Task 2 我们考虑 dfs 后对于深度相同的节点进行配对。
对于同一深度的点对配对一定不存在导出子图的边,而深度差 (1) 的点对配对最多存在 (2) 条导出子图上的边。
时间复杂度 (O(n))
#include <cstdio>
#include <vector>
#include <functional>
using namespace std;
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, m;
scanf("%d %d", &n, &m);
vector<vector<int>> g(n);
for (int i = 0; i < m; ++i) {
int u, v;
scanf("%d %d", &u, &v);
--u, --v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> dep(n), fa(n);
int max_dep = 0;
function<void(int u, int fa)> dfs = [&](int u, int fp) {
dep[u] = dep[fp] + 1;
fa[u] = fp;
if (dep[u] > dep[max_dep])
max_dep = u;
for (auto v : g[u])
if (!dep[v])
dfs(v, u);
};
dfs(0, 0);
if (dep[max_dep] >= (n + 1) / 2) {
printf("PATH
%d
", dep[max_dep]);
int u = max_dep;
do
printf("%d ", u + 1);
while (u = fa[u]);
printf("1");
} else {
printf("PAIRING
");
vector<vector<int>> dp(dep[max_dep]);
for (int i = 0; i < n; ++i) dp[dep[i] - 1].push_back(i);
int ans = 0;
for (int i = 0; i < dep[max_dep]; ++i) ans += (dp[i].size() >> 1);
printf("%d
", ans);
for (int cur_dep = 0; cur_dep < dep[max_dep]; ++cur_dep)
for (unsigned i = 0; i < dp[cur_dep].size() - 1; i += 2) printf("%d %d
", dp[cur_dep][i] + 1, dp[cur_dep][i + 1] + 1);
}
puts("");
}
return 0;
}