SMU Autumn 2024 Personal Round 2
SMU Autumn 2024 Personal Round 2
A. Not Adjacent Matrix
思路
可以按照奇数列就向上移动一个元素,溢出的元素补到最后一行,这样构造后检查一下是否有相邻元素即可(事实上只有 \(n=1\) 才会无解)。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n;
cin >> n;
vector a(n + 1, vector<int>(n + 1));
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
a[i][j] = (i - 1) * n + j;
}
}
for (int i = 1; i <= n; i ++) {
if (i & 1) {
auto t = a[1][i];
for (int j = 2; j <= n; j ++) {
a[j - 1][i] = a[j][i];
}
a[n][i] = t;
}
}
for (int i = 1; i <= n; i ++) {
for (int j = 1; j < n; j ++) {
if (a[i][j] == a[i][j + 1] + 1 || a[i][j] == a[i][j + 1] - 1) {
cout << -1 << '\n';
return ;
}
}
}
for (int i = 1 ; i <= n; i ++)
for (int j = 1; j <= n; j ++)
cout << a[i][j] << " \n"[j == n];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
B. Alyona and Numbers
思路
要求 $(x+y) \equiv0\pmod{5} $ 的数量,其实只要看 \(x,y\) 的余数互补情况即可。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
int M[6] {};
for (int i = 1; i <= m; i ++) {
M[i % 5]++;
}
i64 ans = 0;
for (int i = 1; i <= n; i ++) {
ans += M[(5 - (i % 5)) % 5];
}
cout << ans << '\n';
return 0;
}
C. Pleasant Pairs
思路
将 \(a_i\)从小到大排序,然后暴力枚举数对匹配即可,当 \(a_i \times a_j \ge 4\times 10^5\) 时退出即可。
复杂度 \(O(nlogn)\)。
因为对于 \(a_i=1\),最多匹配 \(\frac {4\times 10 ^ 5}{1}\) 次,\(a_i = 2\),最多匹配 \(\frac {4\times 10 ^ 5}{2}\) 次,\(\dots\),对于 \(a_i=k\),最多匹配 \(\frac {4\times 10 ^ 5}{k}\) 次,即不会跑满 \(O(n^2)\),近似 \(O(nlogn)\) 。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n;
cin >> n;
vector<array<i64, 2>> a(n + 1);
for (int i = 1; i <= n; i ++) {
i64 x;
cin >> x;
a[i] = {x, i};
}
sort(a.begin() + 1, a.end());
i64 ans = 0;
for (int i = 1; i <= n; i ++) {
auto [x, xi] = a[i];
for (int j = 1; j < i; j ++) {
auto [y, yi] = a[j];
if (x * y > 2 * n) break;
if (x * y == xi + yi) {
ans ++;
}
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
D. Hossam and Friends
思路
双指针维护题目所说的区间,对于一个新的 \(r\) ,判断能不能扩展到它,可以用 \(multiset\) 维护朋友关系,如果存在该关系,则可以扩展到它,否则就停止。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n, m;
cin >> n >> m;
vector g(n + 1, vector<int>());
for (int i = 1; i <= m; i ++) {
int u, v;
cin >> u >> v;
if (u > v) swap(u, v);
g[u].push_back(v);
}
i64 ans = 0;
multiset<int> s;
for (int l = 1, r = 0; l <= n; l ++) {
while (r + 1 <= n && !s.count(r + 1)) {
r ++;
for (auto v : g[r]) {
s.insert(v);
}
}
i64 len = r - l + 1;
ans += len;
for (auto v : g[l]) {
s.erase(s.lower_bound(v));
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
E. Same Count One
思路
首先记所有 \(1\) 的数量为 \(num\),那么显然有当 \(n\mod num≠0\) 时无解。那么考虑有解的时候该怎么办。
显然对于每一个 \(a_i\) 序列中,最终 \(1\) 的数量为 $num $,记作 \(t\);并记 \(cnt_i\) 表示 \(a_i\) 序列中 \(1\) 的数量。
我们希望最终所有的 \(cnt_i\) 都等于 \(t\),并且希望操作步数最小,我们考虑一个显然的贪心:将 \(cnt_i>t\) 的序列中的 \(1\) 补 \(cnt_j<t\) 缺失的 \(1\)。
这样我们每一次的操作都会使 \(∑_{i=1}^n|cnt_i−t|\) 减少 \(2\),显然是最优的方案。
该题解转载:https://www.cnblogs.com/WaterSunHB/p/17962528/CF1774D
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n, m;
cin >> n >> m;
int cnt = 0;
vector bnariy(n, vector<int>(m));
vector<int> one(n);
for (int i = 0; i < n; i ++) {
for (int j = 0; j < m; j ++) {
cin >> bnariy[i][j];
one[i] += bnariy[i][j] == 1;
}
cnt += one[i];
}
if (cnt % n != 0) {
cout << -1 << '\n';
return ;
}
int avg = cnt / n;
vector<array<int, 3>> ans;
for (int j = 0; j < m; j ++) {
vector<int> has, need;
for (int i = 0; i < n; i ++) {
if (one[i] > avg && bnariy[i][j]) {
has.emplace_back(i);
} else if (one[i] < avg && !bnariy[i][j]) {
need.emplace_back(i);
}
}
while (has.size() && need.size()) {
auto x = has.back(), y = need.back();
ans.push_back({x + 1, y + 1, j + 1});
one[x] --, one[y] ++;
has.pop_back(), need.pop_back();
}
}
cout << ans.size() << '\n';
for (auto [x, y, z] : ans) {
cout << x << ' ' << y << ' ' << z << '\n';
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
F. Shuffling Songs
思路
状压DP。
不过我用并查集减掉了很多不合法的状态,比如它不是个连通块。
然后就是把状态中的点拿出来,如果存在边则连上,最后对存在的点做状压DP即可。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
void solve() {
int n;
cin >> n;
int cnt = 0;
unordered_map<string, int> mp;
vector<pair<int, int>> a;
for (int i = 0; i < n; i ++) {
string s1, s2;
cin >> s1 >> s2;
if (!mp.count(s1)) {
mp[s1] = ++cnt;
}
if (!mp.count(s2)) {
mp[s2] = ++cnt;
}
a.emplace_back(mp[s1], mp[s2]);
}
vector edge(n, vector<int>(n));
for (int i = 0; i < n; i ++) {
auto [s1, s2] = a[i];
for (int j = 0; j < i; j ++) {
auto [w1, w2] = a[j];
if (s1 == w1 || s2 == w2) {
edge[i][j] = edge[j][i] = 1;
}
}
}
int ans = n - 1;
for (int i = 0; i < (1 << n); i ++) {
if (__builtin_popcount(i) >= ans) {
continue;
}
vector<int> now;
for (int j = 0; j < n; j ++) {
if (i >> j & 1) continue;
now.emplace_back(j);
}
int m = n - __builtin_popcount(i);
vector g(m, vector<int>());
DSU d(m);
for (int j = 0; j < m; j ++) {
for (int p = 0; p < m; p ++) {
if (edge[now[j]][now[p]]) {
g[j].emplace_back(p);
g[p].emplace_back(j);
d.merge(j, p);
}
}
}
if (d.size(0) != m) {
continue;
}
const int inf = 100;
vector dp(1 << m, vector<int>(m, inf));
for (int j = 0; j < m; j ++) {
dp[1 << j][j] = 1;
}
auto check = [&](int st)->bool{
for (int k = 0; k < m; k ++) {
if (st >> k & 1) {
for (auto p : g[k]) {
if (!(st >> p & 1)) {
auto nst = st | (1 << p);
dp[nst][p] = min(dp[nst][p], dp[st][k] + 1);
if (nst == (1 << m) - 1 && dp[nst][p] == m) {
ans = __builtin_popcount(i);
return true;
}
}
}
}
}
return false;
};
for (int st = 0; st < (1 << m); st ++) {
if (check(st)) {
break;
}
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}