Loading

Codeforces 1970B3 Exact Neighbours (Hard)

Easy 没什么启发性,直接考虑 Medium

考虑到 \(a_1 = 0\),那么 \(1\) 明显直接和自己配对就行,考虑分配到一个特殊的位置 \((1, 1)\)

接下来考虑如果还有 \(a_i = 0\),那么明显 \(i\) 也是和自己配对,此时因为这是无关紧要的就可以离特殊的 \((1, 1)\) 尽量远一点,也就是让 \(x\) 坐标尽量大。
同时如果有 \(a_i = a_j\),那么就可以让 \(i, j\) 互相配对,还是一样尽量远,可以让两个的 \(x\) 坐标就相差 \(1\),构造 \((x, 1), (x + 1, a_i)\) 即可。

剩下的不同的 \(a_i\) 的取值肯定就只存在一个 \(a_i\) 了。
这个时候考虑到 \((2, y)\)\((1, i)\) 的距离可以为 \(1\sim n\)\((3, y)\)\((2, i)\) 的距离可以为 \(2\sim n + 1\)……
那么就可以把剩下的 \(a_i\) 从小到大排序,然后 \(x\)\(2\) 开始依次往大了去填,因为 \(a_i\le n\) 且因为每种取值最多存在一个有 \(a_i\ge x - 1\),肯定可以分配出 \(y\)

那么就构造完了,按照这个方式可以知道肯定有解。

时间复杂度 \(\mathcal{O}(n)\)

代码写丑了写成了 \(\mathcal{O}(n\log n)\),把 map 换成桶即可。

#include<bits/stdc++.h>
const int maxn = 2e5 + 10;
int a[maxn];
int dx[maxn], dy[maxn], to[maxn];
bool vis[maxn];
int main() {
   int n;
   scanf("%d", &n);
   for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
   dx[1] = dy[1] = to[1] = 1;
   std::map<int, int> mp;
   int w = n;
   for (int i = 2; i <= n; i++) {
      if (! a[i]) {
         dx[i] = w--, dy[i] = 1, to[i] = i;
      } else {
         if (mp[a[i]]) {
            int j = mp[a[i]];
            dx[i] = w--, dx[j] = w--, to[i] = j, to[j] = i;
            dy[i] = 1, dy[j] = a[i];
            mp[a[i]] = 0;
         } else
            mp[a[i]] = i;
      }
   }
   w = 2;
   for (auto _ : mp) {
      int v = _.first, id = _.second;
      if (! id) continue;
      dx[id] = w, dy[id] = 1 + v - (w - 1), to[id] = 1, w++;
   }
   puts("YES");
   for (int i = 1; i <= n; i++) printf("%d %d\n", dx[i], dy[i]);
   for (int i = 1; i <= n; i++) printf("%d ", to[i]);
   return 0;
}

接下来考虑 Hard

首先如果存在 \(a_i = 0\),那么还是像 Medium 那样做就行了。

否则考虑用一些其他的替代掉 \(a_i = 0\) 的作用。

一个想法是对于 \(a_i = a_j\) 的,可以分配到 \((1, 1), (2, a_i)\),然后就像 Medium 一样构造。
但是能发现这个有个小问题,就是因为 \(x = 2\) 已经被占了,导致如果存在 \(a_k = 1\) 这个 \(k\) 没有位置了。
但是可以考虑把 \(k\) 拎出来,先填完 \(2\sim n\) 的。
因为 \(a_k = 1\) 实际上就是 \(x\) 坐标差 \(1\)\(y\) 坐标相同,设填完 \(2\sim n\) 最后一个填的的坐标是 \((x, y)\),给 \(k\) 分配 \((x + 1, y)\) 即可。
还有一种方式是一开始分配成 \((1, a_i), (2, 1)\),然后特殊点就变为 \((2, 1)\),就是完全一样的了。

发现还是会漏掉情况。
但是能发现漏掉的情况只会有 \(1\sim n\) 都只刚好出现 \(1\) 次。
这个时候能发现 \(n = 2\) 时无解。
否则对于 \(a_i = 1, a_j = 2, a_k = 3\),可以分别构造出 \((1, 1), (2, 1), (3, 2)\),其中 \(i\to j, j\to k, k\to i\)
然后对于 \(4\sim n\) 的和其他的一样构造即可。

时间复杂度 \(\mathcal{O}(n)\)

#include<bits/stdc++.h>
const int maxn = 2e5 + 10;
int a[maxn];
int dx[maxn], dy[maxn], to[maxn];
int lst[maxn];
int main() {
   int n;
   scanf("%d", &n);
   for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
   int f = 0, fid = 0, bk = n, edy = 0, edid = 0;
   for (int i = 1; i <= n; i++) {
      if (! a[i]) {
         if (! f) dx[i] = ++f, fid = i, edy = 1, edid = i;
         else dx[i] = bk--;
         dy[i] = 1, to[i] = i;
      } else if (lst[a[i]]) {
         int &j = lst[a[i]];
         if (! f) dx[i] = ++f, dx[j] = ++f, fid = i, edy = a[i], edid = j;
         else dx[i] = bk--, dx[j] = bk--;
         dy[i] = 1, dy[j] = a[i], to[i] = j, to[j] = i;
         j = 0;
      } else lst[a[i]] = i;
   }
   if (! f) {
      if (n == 2)
         return puts("NO");
      int &x = lst[1], &y = lst[2], &z = lst[3];
      f = 3, fid = x, edy = 2, edid = z;
      dx[x] = 1, dy[x] = 1;
      dx[y] = 2, dy[y] = 1;
      dx[z] = 3, dy[z] = 2;
      to[x] = y, to[y] = z, to[z] = x;
      x = y = z = 0;
   }
   for (int i = 2; i <= n; i++)
      if (lst[i]) {
         int p = lst[i];
         dx[p] = ++f, dy[p] = 1 + i - (f - 1), to[p] = fid;
         edy = dy[p], edid = p;
      }
   if (lst[1]) {
      int p = lst[1];
      dx[p] = ++f, dy[p] = edy, to[p] = edid;
   }
   puts("YES");
   for (int i = 1; i <= n; i++) printf("%d %d\n", dx[i], dy[i]);
   for (int i = 1; i <= n; i++) printf("%d ", to[i]);
   return 0;
}
posted @ 2024-05-04 21:45  rizynvu  阅读(5)  评论(0编辑  收藏  举报