题目链接: 号码牌
大致题意
给定一个长度为 n n n的序列, 第 i i i个位置的值为 a i a_i ai. (保证 a a a是 1 1 1~ n n n的一个排列)
每个位置还有一个值 d i d_i di, 若满足 ∣ i − j = d i ∣ |i-j=d_i| ∣i−j=di∣, 表示位置 i i i和位置 j j j可以进行任意次交换.
问: 能否使得最终的序列满足 a i = i a_i = i ai=i.
解题思路
并查集 (数据太小了, 比赛时写了个Floyd)
由于两个位置的交换次数是任意次. 因此, 如果 x x x和 y y y可以交换, 且 y y y和 z z z可以交换, 则 x , y , z x, y, z x,y,z三个位置处的 a a a值可以互换.
上述分析可以推广到一个有限大小的连通集合中.
因此, 我们只需要判断每个连通集合内部 a i a_i ai值是否和该集合下标 i i i相同即可.
Floyd
设 m p [ ] mp[] mp[], 其中 m p [ v a l ] mp[val] mp[val]记录 v a l val val元素所处原序列的位置.
我们考虑题目的本质: 如果有 a i ≠ i a_i \ne i ai=i, 表明 i i i位置需要与 m p [ a [ i ] ] mp[a[i]] mp[a[i]]位置进行交换. 即: 我们需要判断 i , j i, j i,j两个位置是否可达. 我们比较容易想到通过多源最短路进行处理.
考虑到本题并不需要求出两点的距离, 只需要判断是否可达, 因此若采用 F l o y d Floyd Floyd算法, 第三层循环可以认为是在求并集, 我们可以通过 b i t s e t bitset bitset进行优化.
写上这个做法, 主要感觉这个题作为 b i t s e t bitset bitset优化 F l o y d Floyd Floyd入门题很友好.
AC代码
并查集DSU
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E2 + 10;
int a[N], d[N];
/* 并查集模版 */
struct DSU {
int p[N];
int find(int x) {
return x == p[x] ? x : p[x] = find(p[x]); }
void merge(int a, int b) {
a = find(a), b = find(b);
if (a == b) return;
p[b] = a;
}
void init(int n) {
rep(i, n) p[i] = i; }
}dsu;
vector<int> v1[N], v2[N];
int main()
{
int n; cin >> n;
rep(i, n) scanf("%d", &a[i]);
rep(i, n) scanf("%d", &d[i]);
dsu.init(n);
rep(i, n) {
int x = i - d[i], y = i + d[i];
if (x >= 1) dsu.merge(i, x);
if (y <= n) dsu.merge(i, y);
}
rep(i, n) v1[dsu.find(i)].push_back(a[i]), v2[dsu.find(i)].push_back(i);
bool flag = 1;
rep(i, n) {
if (i != dsu.find(i)) continue;
sort(v1[i].begin(), v1[i].end());
if (v1[i] != v2[i]) flag = 0;
}
puts(flag ? "YES" : "NO");
return 0;
}
Floyd + bitset
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E2 + 10;
int a[N], d[N], mp[N];
bitset<N> can[N];
void fact(int a, int b) {
can[a][b] = can[b][a] = 1; }
int main()
{
int n; cin >> n;
rep(i, n) scanf("%d", &a[i]), mp[a[i]] = i;
rep(i, n) {
scanf("%d", &d[i]);
if (i - d[i] > 0) fact(i, i - d[i]);
if (i + d[i] <= n) fact(i, i + d[i]);
can[i][i] = 1;
}
rep(k, n) rep(i, n) {
if (can[i][k]) can[i] |= can[k];
}
bool flag = 1;
rep(i, n) flag &= can[i][mp[i]];
puts(flag ? "YES" : "NO");
return 0;
}