acwing.第72场周赛 t3最小移动距离
AcWing 4626. 最小移动距离
原题链接:https://www.acwing.com/problem/content/4629/
思路
要求对于每一个点x都满足走过t,到达一个目标点y.并且x和y都可以互为目标点。
找出t的最小值。
每一个点的出度为1.易得,互为目标点的点必须在同一个环上才可以满足条件。
维护一个并查集(一个集合就是一个环),维护每个点的入度,维护每个并查集的大小(环的长度)
只看一个环的t:
如果一个环中点的个数为偶数个,那么答案可以是len/2
如果一个环中点的个数为奇数个,那么答案只能是len
n个点可以分为若干个环,最终的答案是这些个环的最小公倍数
此题需要考虑,n分为若干个数之后,这若干个数的最小公倍数会不会爆int,会不会爆long long(爆longlong要开高精度)
n = [a1,a2,...ak],要使得a1 * a2 * ... * ak最大,把n尽量分得更多的3,剩下的用2去凑,这样的使得积最大(结论).
本题n最大100,那分为32个3,2个2。最坏情况为3^32 * 4 =>9 ^ 16 * 4 小于 10 ^ 16 * 4,不会爆longlong
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N = 110;
int d[N],p[N],s[N]; // 记录入度,并查集数组,环的长度
int n;
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]); // x的父亲结点置为祖宗结点
return p[x];
}
LL gcd(LL a,LL b)
{
return b ? gcd(b,a%b) : a;
}
LL get()
{
// 检查所有入度是否都是1(是否都是环)
for(int i = 1; i <= n; i++)
{
if(d[i] != 1) return -1;
}
// 计算答案 每个环的最小公倍数
LL res = 1;
for(int i = 1; i <= n;i ++)
{
if(p[i] == i) // 如果是环的起点
{
int len = s[i];
if(len % 2 == 0) len /= 2;
res = (res * len) / gcd(res,len);
}
}
return res;
}
int main()
{
cin >> n;
// 并查集初始化
for(int i = 1; i <= n; i ++)
{
p[i] = i;
s[i] = 1;
}
// 并查集合并
for(int i = 1; i <= n; i ++)
{
int x;
cin >> x;
d[x] ++; // x入度+1
int a = find(i),b = find(x);
if(a != b) // 如果没有在一个集合里就放进
{
p[a] = b;
s[b] = s[b] + s[a];
}
}
cout << get() << endl;
return 0;
}
rds_blogs