Ural 1682 Crazy Professor (并查集)

一位数学教授疯狂地在黑板上写下正整数,并根据特定的数学条件将它们连接起来,形成一个图。任务是确定何时这个过程会因为形成循环而停止。本文介绍了一个算法解决方案,使用并查集来检测循环。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

Professor Nathan Mathan is crazy about mathematics. For an unknown reason, he started to write on the blackboard all positive integers starting from 1. After writing a new number a, Professor draws lines connecting it with all the numbers b that are already on the blackboard and satisfy at least one of the conditions:

  • b + a · a ≡ 0 (mod k),
  • a + b · b ≡ 0 (mod k),

where k is some parameter.

Nobody can persuade him to stop this meaningless procedure. Professor says that he will stop as soon as there appears a cycle in the graph of the numbers on the blackboard. But only Professor knows when that will happen and whether it will happen at all. Help his colleagues determine after which number he will stop.

Input

You are given the integer k (1 ≤ k ≤ 100000).

Output

Output the number after which the first cycle will appear in the graph. If it never happens, output −1.

题意

Professor 想要在黑板上从 1 开始依次写下每一个正整数。每写下一个正整数 a ,他将判断在之前写下的数字中是否存在 b ,使得满足下列任意一个条件:

  • a+b×b0(mod k)
  • b+a×a0(mod k)

找到所有 b ,并连接一条有向边 (a,b) 。当所连接的某些边形成环路时,停止。

给定模数 k ,问对于每个询问 k 的停止时写下的正整数 a 。若永远不会停止,则输出 -1

分析

在无从下手的时候,考虑先找到某些特殊点。通过二式 b+a×a0(mod k) 。可以判断出存在有向边 (k1,1), (2k1,1), (3k1,1) … 通过一式可以看到

a=k1b=2k1a+b×b?(mod k)(k1)+(2k1)20(mod k)

故又存在有向边 (2k1,k1) 。此时必然存在环路。此题对于任意的 k 都必然存在解,且最大为 2k1

在明确上述问题后,暴力扫描 [1, 2k-1] 即可。对于判断环路,可通过并查集解决,当准备直接相连的两边已经在同一个集合内时,说明出现环路。

代码

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
const int N = 200000 + 10;
long long k;
map<pair<int, int>, bool> mp;
vector<int> vec[100010];
int fa[N];
int find(int x)
{
    return x = (x == fa[x] ? x : find(fa[x]));
}
bool Union(int x, int y)
{
    if(mp[make_pair(x, y)]) return false;
    mp[make_pair(x, y)] = 1;
    int fx = find(x), fy = find(y);
    if(fx == fy)    return true;
    if(fx > fy) swap(fx, fy);
    fa[fy] = fx;
    return false;
}
bool solve(long long x)
{
    long long b = k - x*x % k;
    if(b == 0)  b += k;
    for(;b < x; b+=k)
    {
        if(Union(b, x))
        {
            printf("%I64d\n", x);
            return true;
        }
    }
    int kb = k-x%k;
    for(int i=0;i<vec[kb].size();i++)
    {
        if(vec[kb][i] >= x) break;
        if(Union(vec[kb][i], x))
        {
            printf("%I64d\n", x);
            return true;
        }
    }
    return false;
}
int main()
{
    scanf("%I64d",&k);
    mp.clear();
    for(int i=1;i<N;i++)
        fa[i] = i;
    for(long long i=1;i<N;i++)
        vec[(int)(i*i%k)].push_back(i);
    for(int i=1;;i++)
    {
        if(solve(i))    break;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值