Codeforces Round #812 (Div. 2) 题解
https://codeforces.com/contest/1713
估计也就我赛中才D都写不出来了
A题
题意:
给定二维平面上\(n\)个点的坐标,保证每个点坐标\(x = 0或者y = 0\),问你从\((0, 0)\)点出发,最少走多少距离可以经过所有点并且回到起点。
思路:
因为每个点坐标\(x = 0或者y = 0\),这样子显然我们在每条轴上走到离起点最远的点再回去就行,并且可以看出来这样走也是最小距离。
void solve() {
int n;
cin >> n;
int maxx, minx, maxy, miny;
maxx = maxy = 0;
minx = miny = 0;
for(int i = 1 ; i <= n ; i ++) {
int x, y;
cin >> x >> y;
maxx = max(maxx, x);
maxy = max(maxy, y);
minx = min(minx, x);
miny = min(miny, y);
}
cout << (maxx + maxy - minx - miny) * 2 << "\n";
}
B题
题意:
给你一个数组,每次可以选择一个区间,区间内的数都减\(1\)。
问你当前数组是否是操作最少次数就能使得整个区间为\(0\)的数组的一种排列。
思路:
很容易发现,要使得操作次数最小,整个数组必须是一个山峰的形状(先非严格单调递增再非严格单调递减),否则就把它变成一个山峰形状操作次数会更少。
int a[N];
int n;
void solve() {
cin >> n;
int maxd = 0, pos = 0;
for(int i = 1 ; i <= n ; i ++) {
cin >> a[i];
if(maxd < a[i]) {
maxd = a[i];
pos = i;
}
}
bool flag = true;
for(int i = 1 ; i < pos ; i ++)
if(a[i + 1] < a[i]) flag = false;
for(int i = pos ; i < n ; i ++)
if(a[i] < a[i + 1]) flag = false;
if(flag) cout << "YES\n";
else cout << "NO\n";
}
C题
题意:
要求构造一个\(0~n-1\)的排列\(p\),使得每个位置上的数\(i + p_i\)是完全平方数.
思路:直接贪心,从\(n - 1 ~ 0\)位置上,然后从大往小放能放的第一个数。
bool st[N];
int ans[N];
int n;
void solve() {
cin >> n;
memset(st, false, sizeof(bool) * (n + 5));
int lim = sqrt(2 * n - 2);
for(int i = n - 1 ; i >= 0 ; i --) {
int s = sqrt(i);
if(s * s != i) s ++;
for(int j = lim ; j >= s ; j --) {
if(j * j - i > n - 1) continue;
if(!st[j * j - i]) {
ans[i] = j * j - i;
st[j * j - i] = true;
break;
}
}
}
for(int i = 0 ; i < n ; i ++) cout << ans[i] << " ";
cout << "\n";
}
D题
题意:
交互题,给出一场有\(2^n\)个人参加淘汰制的锦标赛,每次可以询问两个人谁胜场多,要求至多询问次\(upper{2/3 * 2^n}\), 找到最终的胜者。
思路:
我们每次把四个人一组进行查询,需要多少次可以查出来四个人力谁是胜者?答案是2次。
假设当前四个人比之前,他们都获胜了\(t\)次
那么比完的结果必然是\(t, t, t+1, t+2\)的一个排列
设四个人分别是 \(x,y,z,k\)
我们先询问\(x, z\)
得\(0\),那么我们再询问\(y, k\)就能得到当前四人的胜者
得\(1\),那么我们再询问\(x, k\)就能得到当前四人的胜者
得\(2\),那么我们再询问\(y, z\)就能得到当前四人的胜者
最后注意剩下两个人的情况即可。
int n;
int query(int x, int y){
cout << "? " << x << " " << y << endl;
int res;
cin >> res;
return res;
}
void solve() {
vector<int> v;
vector<int> temp;
cin >> n;
n = 1 << n;
for(int i = 1 ; i <= n ; i ++) v.push_back(i);
while(v.size() > 2) {
temp.clear();
for(int i = 0 ; i < v.size() ; i += 4){
int x = v[i], y = v[i + 1], z = v[i + 2], k = v[i + 3];
int t = query(x, z);
if(t == 0) {
t = query(y, k);
if(t == 1) temp.push_back(y);
else temp.push_back(k);
}
else if(t == 1) {
t = query(x, k);
if(t == 1) temp.push_back(x);
else temp.push_back(k);
} else {
t = query(y, z);
if(t == 1) temp.push_back(y);
else temp.push_back(z);
}
}
v = temp;
}
if(v.size() == 1) cout << "! " << v[0] << endl;
else {
int t = query(v[0], v[1]);
if(t == 1) cout << "! " << v[0] << endl;
else cout << "! " << v[1] << endl;
}
}
E题
题意:
给出一个矩阵,每次可以选择一个\(k\),然后交换所有\(a[k][i]\)和\(a[i][k]\),问能得到的最小字典序是多少。
思路:
首先肯定越前面的数越重要,因此我们只需要按顺序判断当前位置\((a[i][j]和a[j][i])\)是否需要交换。
因为每个位置的交换可以选择\(k = i或者k=j\)。
因此每个要交换的位置,\(k\)只能二选一,而每个不需要的位置,可以两个位置都不交换或者两个位置都交换。
用扩展域并查集,详见代码。
int p[N * 2];
int n;
int a[N][N];
int find(int x){
if(x != p[x])
p[x] = find(p[x]);
return p[x];
}
bool same(int x, int y) {
x = find(x), y = find(y);
return x == y;
}
void merge(int x, int y) { // 让 x 连到 y 上
int fx = find(x), fy = find(y);
p[fx] = fy;
}
void solve() {
cin >> n;
for(int i = 1 ; i <= n * 2 ; i ++) p[i] = i;
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]) { // i 和 j 有一个要交换 交换i或者交换j(敌人)
if(same(i, j)) continue; // 如果 i 和 j 是朋友
merge(i, j + n); // i 连到 j + n 上 find(i) > n
merge(i + n, j); // i + n 连到 j 上 find(j) <= n
}
else if(a[i][j] < a[j][i]) { // i 和 j 都不要交换 或者 同时交换 (朋友)
if(same(i, j + n)) continue; // 如果 i 和 j 是敌人
merge(i, j); // i 连到 j 上 find(i) = find(j)
merge(i + n, j + n); // i + n 连到 j + n 上
}
}
for(int k = 1 ; k <= n ; k ++) { // 从敌对关系中任选一种交换 find(i) > n or find(j) <= n
if(find(k) <= n) continue; // 选择 find(i) > n 的交换, find(i) <= n 的不交换
// if(find(k) > n) continue;
for(int i = 1 ; i <= n ; i ++)
swap(a[k][i], a[i][k]);
}
for(int i = 1 ; i <= n ; i ++) {
for(int j = 1 ; j <= n ; j ++)
cout << a[i][j] << " ";
cout << "\n";
}
}