Codeforces Round 812 (Div. 2)
中间困了好久,, D就不想做
然而晚上又睡不着,于是起来补
C. Build Permutation
懵了半天没发现规律,打个表才发现..
可以分段搞,开始的上限 是
我们设 ,且 都是完全平方数
不难发现,如果 ,直接按 的顺序把下标 排一遍就好
然后考虑 的部分,这部分要成为完全平方数只能变到 ,但是 加上 是不行的
我在这里卡了好久
然后打表发现从后往前考虑补成对应的平方数就好了,代码比较清楚
#include <bits/stdc++.h>
using namespace std;
int main() {
int tt;
cin >> tt;
while (tt--) {
int n;
cin >> n;
vector<int> ans(n);
int now = n - 1;
while (now > 0) {
int l1 = sqrt(now);
int l2 = sqrt(now) + 1;
// cout << l2 << ' ' << now << '\n';
if (l1 * l1 == now) {
for (int i = 0; i <= now; ++i)
ans[i] = l1 * l1 - i;
break;
}
for (int i = now; l2 * l2 - i <= now; --i)
ans[i] = l2 * l2 - i;
now = l2 * l2 - now - 1;
}
for (int i = 0; i < n; ++i)
cout << ans[i] << " \n"[i == n - 1];
}
return 0;
}
D. Tournament Countdown
一共有 个人
如果按照题意一轮轮询问下去,次数就是
而题目要求的是不超过 次询问,显然寄了
对于询问a b,给定的是赢的次数,第一轮会淘汰 个人,
即有 个人赢 0 次,
一直到第 轮,还剩 1 个人,赢了 次
因此我们询问 (a, b),就会得到多种情况
- 相等,说明a b都被淘汰了
- a>b,说明b被淘汰
- 反之a被淘汰
并且对于同一层的 (a, b), (c, d),询问必从两组中各挑一个,这样相等说明ab 都被淘汰,因为b d肯定决出胜负,不等则询问胜者与败者组的另一个
这样2次询问能把两组4个人变为1个人
令
经过 次询问会使 缩小到 (0 or 1) 的范围
得到总次数为 ,分类讨论一下
的值,发现都是成立的
#include <bits/stdc++.h>
#define m (1 << n)
using namespace std;
int main() {
int tt;
cin >> tt;
while (tt--) {
int n;
cin >> n;
auto ask = [&](int &x, int &y) {
cout << "? " << x << ' ' << y << endl;
int tem;
cin >> tem;
return tem;
};
vector<int> a(m);
for (int i = 0; i < m; ++i)
a[i] = i + 1;
while (m >= 4) {
vector<int> b;
for (int i = 0; i < m; i += 4) {
int t1 = ask(a[i], a[i + 2]), t2;
if (t1 == 0) {
t2 = ask(a[i + 1], a[i + 3]);
b.push_back(t2 == 1 ? a[i + 1] : a[i + 3]);
}
else if (t1 == 1) {
t2 = ask(a[i], a[i + 3]);
b.push_back(t2 == 1 ? a[i] : a[i + 3]);
}
else {
t2 = ask(a[i + 1], a[i + 2]);
b.push_back(t2 == 1 ? a[i + 1] : a[i + 2]);
}
}
a = b;
n -= 2;
}
if (a.size() == 1)
cout << "! " << a[0] << endl;
else {
int ans = (ask(a[0], a[1]) == 1) ? a[0] : a[1];
cout << "! " << ans << endl;
}
}
return 0;
}
E. Cross Swapping
发现如果令 进行两次操作, 的位置不会改变,剩下的则是正常的行列交换
因此不难发现如果 全部操作一遍,不会改变这个矩阵
因此如果贪心的考虑每一位,优先满足第一行字典序最小,那么只要考虑 的情况就好了,其中
,令这样的位置为集合
显然我们在 里面进行决策,不会改变第一行的情况
考虑 ,对 进行操作会影响到第 行与第 列的元素,然后发现对于第二列,也是如此,如果第二列交换与不交换都是这样,那么就作为改变第三行答案的选项留在集合里
逝了世发现上面那个想法假了,,wa 2但是我看不到数据,有高手看到可以Hack一下我..
爬去看题姐..
简单讲就是说,从前往后考虑,然后每个i考虑操作或者不操作,那么在满足前面的关系上,顺序对每个位置涉及到的 操作一下
然后用并查集维护就好了、、
扩展域还是边带权都一样
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int tt;
cin >> tt;
while (tt--) {
int n;
cin >> n;
vector a(n + 1, vector<int>(n + 1, 0));
vector fa(2 * n + 1, 0);
iota(fa.begin(), fa.end(), 0);
function<int(int)> find = [&](int o) {
return o == fa[o] ? o : fa[o] = find(fa[o]);
};
function<void(int, int)> merge = [&](int u, int v) {
if (find(u) == find(v))
return;
fa[find(v)] = find(u);
};
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
cin >> a[i][j];
for (int i = 1; i <= n; ++i)
for (int j = i + 1; j <= n; ++j) {
if (a[i][j] == a[j][i] || find(i) == find(j) || find(i) == find(j + n))
continue;
if (a[i][j] < a[j][i]) {
merge(i, j);
merge(i + n, j + n);
} else {
merge(i, j + n);
merge(i + n, j);
}
}
for (int i = 1; i <= n; ++i) {
if (find(i) <= n) {
// deb(i);
for (int j = 1; j <= n; ++j)
swap(a[i][j], a[j][i]);
}
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
cout << a[i][j] << " \n"[j == n];
}
return 0;
}
中间非常诡异的是这个,第46行的merge如果换一下顺序,就会wa
仔细思考一下,因为我的merge写法导致如果调换顺序,事实上并没有把 分割开..