AcWing4084 号码牌 (并查集 / bitset优化Floyd)

5 篇文章 0 订阅
4 篇文章 0 订阅

题目链接: 号码牌

大致题意

给定一个长度为 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| ij=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;
}

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值