A
答案顯然就是 \(\text{lcm}(a,b)\)。
void run() {
int T = read();
while (T--) {
int a = read(), b = read();
cout << a / __gcd(a, b) * b << '\n';
}
}
B
考慮貪心模擬。若當前列舉到位置 \(i\) 時恰有 \(m\) 個連續的 \(0\),則從這個位置開始將後面連續的 \(k\) 個位置全部賦值為 \(1\)。容易證明這是正確的。
時間複雜度為 \(O(n)\)。
char s[N];
void run() {
int T = read();
while (T--) {
int n = read(), m = read(), k = read();
scanf("%s", s + 1);
int cnt = 0, sum = 0;
for (int i = 1; i <= n; ++i) {
if (s[i] == '0') {
++cnt;
if (cnt >= m) {
++sum;
int j, x;
for (j = i, x = 1; j <= n && x <= k; ++j, ++x)
s[j] = '1';
i = j - 1;
cnt = 0;
}
} else {
cnt = 0;
}
}
cout << sum << '\n';
}
}
C
考慮這樣的一個策略:
- 若存在一個只包含
?
的四連通塊滿足該塊內?
的數目不小於 \(2\),則考慮任選兩個相鄰的?
並讓它們互相只想對方,其餘的?
都指向這兩個?
中的一個。 - 若某一個
?
四聯通的某一個位置不是?
,且從該位置出發會無限迴圈,則讓這個?
指向這個位置。 - 剩下的位置隨便指向任意一個位置。
容易證明該策略正確。若把指向關係顯示建圖則形成一個基環森林,維護的話可以使用並查集和 dfs 找環。總時間複雜度為 \(O(\alpha nm)\)。
程式碼可能有點難寫。
char s[2010][2010];
int id[2010][2010], fa[N], fm[N];
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
const int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
int vis[2010][2010], n, m, dfn;
int dfs(int x, int y) {
assert(s[x][y] != '?');
if (x < 1 || y < 1 || x > n || y > m) return 0;
if (vis[x][y] == dfn)
return 1;
else if (vis[x][y] && fm[id[x][y]])
return 1;
else if (vis[x][y])
return 0;
vis[x][y] = dfn;
if (s[x][y] == 'U') {
if (dfs(x - 1, y)) {
fm[id[x][y]] = 1;
return 1;
}
}
if (s[x][y] == 'D') {
if (dfs(x + 1, y)) {
fm[id[x][y]] = 1;
return 1;
}
}
if (s[x][y] == 'L') {
if (dfs(x, y - 1)) {
fm[id[x][y]] = 1;
return 1;
}
}
if (s[x][y] == 'R') {
if (dfs(x, y + 1)) {
fm[id[x][y]] = 1;
return 1;
}
}
return 0;
}
void run() {
// freopen("1.in", "r", stdin);
// freopen("1.out", "w", stderr);
int T = read(), ca = 1;
while (T--) {
n = read(), m = read();
for (int i = 1; i <= n; ++i)
scanf("%s", s[i] + 1);
// if (ca++ == 41) {
// for (int i = 1; i <= n; ++i, cerr << '\n')
// for (int j = 1; j <= m; ++j) cerr << s[i][j];
// }
dfn = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (s[i][j] == '?')
for (int d = 0; d < 4; ++d) {
int ii = i + dx[d], jj = j + dy[d];
if (ii >= 1 && ii <= n && jj >= 1 && jj <= m && s[ii][jj] == '?') {
if (d == 0) s[i][j] = 'R', s[ii][jj] = 'L';
else if (d == 2) s[i][j] = 'L', s[ii][jj] = 'R';
else if (d == 1) s[i][j] = 'D', s[ii][jj] = 'U';
else s[i][j] = 'U', s[ii][jj] = 'D';
break;
}
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (s[i][j] == '?')
for (int d = 0; d < 4; ++d) {
int ii = i + dx[d], jj = j + dy[d];
if (ii >= 1 && ii <= n && jj >= 1 && jj <= m) {
if (s[ii][jj] != '?') {
int ok = 0;
if (s[ii][jj] == 'L' && d == 0) s[i][j] = 'R', ok = 1;
else if (s[ii][jj] == 'R' && d == 2) s[i][j] = 'L', ok = 1;
else if (s[ii][jj] == 'U' && d == 1) s[i][j] = 'D', ok = 1;
else if (s[ii][jj] == 'D' && d == 3) s[i][j] = 'U', ok = 1;
if (ok) break;
}
}
}
// for (int i = 1; i <= n; ++i, cout << '\n')
// for (int j = 1; j <= m; ++j) cout << s[i][j];
int idx = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
++idx;
id[i][j] = idx;
fa[idx] = idx;
fm[idx] = 0;
vis[i][j] = 0;
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (s[i][j] != '?')
++dfn, dfs(i, j);
// for (int i = 1; i <= n; ++i, cout << '\n')
// for (int j = 1; j <= m; ++j) cout << s[i][j];
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (s[i][j] != '?') {
int ii, jj;
if (s[i][j] == 'L') ii = i, jj = j - 1;
else if (s[i][j] == 'R') ii = i, jj = j + 1;
else if (s[i][j] == 'U') ii = i - 1, jj = j;
else ii = i + 1, jj = j;
if (ii >= 1 && ii <= n && jj >= 1 && jj <= m) {
int x = find(id[i][j]), y = find(id[ii][jj]);
if (x != y) fm[y] |= fm[x], fa[x] = y;
}
}
// for (int i = 1; i <= n * m; ++i) cout << fm[i] << ' ';
// cout << '\n';
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (s[i][j] == '?')
for (int d = 0; d < 4; ++d) {
int ii = i + dx[d], jj = j + dy[d];
if (ii >= 1 && ii <= n && jj >= 1 && jj <= m) {
assert(s[ii][jj] != '?');
if (fm[find(id[ii][jj])]) {
if (d == 0) s[i][j] = 'R';
else if (d == 2) s[i][j] = 'L';
else if (d == 1) s[i][j] = 'D';
else s[i][j] = 'U';
int x = find(id[ii][jj]), y = find(id[i][j]);
if (x != y) fm[y] |= fm[x], fa[x] = y;
break;
}
}
}
int cnt = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
cnt += fm[find(id[i][j])];
// for (int i = 1; i <= n; ++i, cout << '\n')
// for (int j = 1; j <= m; ++j) cout << s[i][j];
cout << cnt << '\n';
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
id[i][j] = 0;
fa[idx] = 0;
s[i][j] = 0;
fm[idx] = 0;
vis[i][j] = 0;
}
}
}
D
比 C 簡單。考慮用 set 來維護當前所有 \(0,1,2\) 的位置。每一次找到最後一個 \(0\) 和最前一個 \(1\)(需要滿足最後一個 \(0\) 在最前一個 \(1\) 後面)或最後一個 \(2\) 和最前一個 \(1\)(需要滿足最後一個 \(2\) 在最前一個 \(1\) 後面)交換。容易證明該策略正確。判斷當前陣列是否有序可以維護當前所有出現過的不同數字,然後用 set 找到當前數字最前和最後的位置就行了。
時間複雜度為 \(O(n\log n)\),自帶大常數(霧
int a[N];
void run() {
int T = read(), ca = 1;
while (T--) {
int n = read();
for (int i = 1; i <= n; ++i)
a[i] = read();
set<int> buc[3];
for (int i = 1; i <= n; ++i)
if (a[i] == 0) buc[0].insert(i);
else if (a[i] == 1) buc[1].insert(i);
else buc[2].insert(i);
auto chk = [&](void) {
int cnt = 0;
vector<int> id;
for (int i = 0; i < 3; ++i)
if (buc[i].size()) ++cnt, id.eb(i);
if (cnt == 1) return 1;
else if (cnt == 2) {
if (*buc[id[0]].rbegin() < *buc[id[1]].begin()) return 1;
return 0;
} else {
if (*buc[0].rbegin() < *buc[1].begin() && *buc[1].rbegin() < *buc[2].begin()) return 1;
return 0;
}
};
vector<pair<int, int>> op;
while (!chk()) {
if (buc[0].size() && buc[1].size()) {
int x = *buc[0].rbegin(), y = *buc[1].begin();
if (x > y) {
op.eb(x, y);
buc[0].erase(x);
buc[1].erase(y);
buc[0].insert(y);
buc[1].insert(x);
}
}
if (chk()) break;
if (buc[1].size() && buc[2].size()) {
int x = *buc[1].rbegin(), y = *buc[2].begin();
if (x > y) {
op.eb(x, y);
buc[1].erase(x);
buc[2].erase(y);
buc[1].insert(y);
buc[2].insert(x);
}
}
}
cout << op.size() << '\n';
for (auto &[x, y] : op)
cout << x << ' ' << y << '\n';
}
}
E
大分類討論。
- \(n=1\)。此時若 \(k=1\) 則需構造排列 \(\lbrace 1\rbrace\)。否則無解。
- \(n\neq 1\) 且 \(k=1\)。此時顯然無解。
- \(2\mid k\)。先考慮 \(k=2\) 的特殊情況。顯然可以構造 \(\lbrace 1,2,3,\ldots,n\rbrace\) 和 \(\lbrace n,n-1,n-2,\ldots,1\rbrace\)。同樣的若 \(2\mid k\) 則可以構造 \(1\sim n\) 組成的排列中字典序前 \(\frac{k}{2}\) 小的排列和字典序前 \(\frac{k}{2}\) 的排列。特殊點,若 \(n!<k\) 則無解。
- \(k-1\mid n-1\)。此時令 \(d=\frac{n-1}{d-1}\),可以構造 \(k\) 個排列。其中第 \(i\) 個排列(\(i<k\))為 \(\lbrace 1+(d-1)i,2+(d-1)i,\ldots,n,1,2,\ldots,(d-1)i\rbrace\)。最後一個排列直接讓每一個位置所對應元素的和相等即可。
- \(2\nmid n\)。此時考慮到 \(k\ge 3\) 且 \(2\nmid k\),所以考慮先用上面的方法構造 \(k=3\) 的排列,對於另外 \(n-3\) 個排列用偶數的構造方法。特殊的,若 \(n!+3<k\) 則無解。
- 其餘情況無解。
時間複雜度為 \(O(nk)\),可以透過。
int sjh[N], p1[N], p2[N], n, k;
void run() {
int T = read(), ca = 1;
while (T--) {
n = read(), k = read();
if (n == 1) {
if (k == 1) {
cout << "YES\n";
for (int i = 1; i <= k; ++i) cout << "1\n";
} else {
cout << "NO\n";
}
} else if (k == 1) cout << "NO\n";
else if (~k & 1) {
int fac = 1;
if (n > 12) fac = LLONG_MAX;
else {
for (int i = 1; i <= n; ++i)
fac = fac * i;
}
if (fac < k) cout << "NO\n";
else {
cout << "YES\n";
iota(a + 1, a + n + 1, 1ll);
for (int i = 0; i < k / 2; ++i) {
for (int j = 0; j < n; ++j) cout << a[j + 1] << ' ';
cout << '\n';
next_permutation(a + 1, a + n + 1);
}
iota(a + 1, a + n + 1, 1ll);
reverse(a + 1, a + n + 1);
for (int i = 0; i < k / 2; ++i) {
for (int j = 0; j < n; ++j) cout << a[j + 1] << ' ';
cout << '\n';
prev_permutation(a + 1, a + n + 1);
}
}
} else {
if (n == k) {
cout << "YES\n";
for (int i = 1; i <= n; ++i, cout << '\n') {
int j = i;
for (int x = 1; x <= n; ++x) {
cout << j << ' ';
++j;
if (j > n) j = 1;
}
}
} else {
if ((n - 1) % (k - 1) == 0) {
cout << "YES\n";
for (int i = 0; i < n; ++i) sjh[i] = 0;
int dt = (n - 1) / (k - 1), st = 1;
while (k--) {
if (k) {
int x = st;
for (int i = 0; i < n; ++i) {
cout << x << ' ';
sjh[i] += x;
++x;
if (x > n) x = 1;
}
cout << '\n';
} else {
int all = st + sjh[0];
for (int i = 0; i < n; ++i)
cout << all - sjh[i] << ' ';
cout << '\n';
}
st += dt;
}
} else {
int fac = 1;
if (n > 12) fac = LLONG_MAX;
else {
for (int i = 1; i <= n; ++i)
fac = fac * i;
}
fac -= 3;
if (fac < k) cout << "NO\n";
else if (n % 2 == 1) {
cout << "YES\n";
vector<vector<int>> p; p.resize(3);
for (int i = 0; i < 3; ++i) p[i].resize(n);
fill(sjh, sjh + n, 0ll);
for (int i = 0; i < n; ++i) p[0][i] = i + 1, sjh[i] = i + 1;
int st = n / 2 + 1; for (int i = 0; i < n; ++i) {
p[1][i] = st; ++st; if (st > n) st = 1;
sjh[i] += p[1][i];
}
int all = n + sjh[0];
for (int i = 0; i < n; ++i) p[2][i] = all - sjh[i];
iota(p1 + 1, p1 + n + 1, 1ll);
iota(p2 + 1, p2 + n + 1, 1ll);
reverse(p2 + 1, p2 + n + 1);
k -= 3; k /= 2;
for (int i = 0; i < 3; ++i, cout << '\n')
for (int j = 0; j < n; ++j) cout << p[i][j] << ' ';
while (k--) {
int o[6] = {1, 1, 1, 1, 1, 1};
for (int i = 1; i <= n; ++i) if (p1[i] != p[0][i - 1]) o[0] = 0;
for (int i = 1; i <= n; ++i) if (p1[i] != p[1][i - 1]) o[1] = 0;
for (int i = 1; i <= n; ++i) if (p1[i] != p[2][i - 1]) o[2] = 0;
for (int i = 1; i <= n; ++i) if (p2[i] != p[0][i - 1]) o[3] = 0;
for (int i = 1; i <= n; ++i) if (p2[i] != p[1][i - 1]) o[4] = 0;
for (int i = 1; i <= n; ++i) if (p2[i] != p[2][i - 1]) o[5] = 0;
if (count(o, o + 6, 1ll) == 0) {
for (int i = 1; i <= n; ++i) cout << p1[i] << ' '; cout << '\n';
for (int i = 1; i <= n; ++i) cout << p2[i] << ' '; cout << '\n';
} else ++k;
next_permutation(p1 + 1, p1 + n + 1);
prev_permutation(p2 + 1, p2 + n + 1);
}
} else {
cout << "NO\n";
}
}
}
}
}
}