Description
Tim拥有控制时间的能力。他学会了BFS后,出了一道题:求出一张无向图中连通块的个数。他想请你做出这道题来
题解
其实跟bfs是没有关系的。
可以知道连边的点的编号是
x
∗
k
i
x*k^i
x∗ki
而在模意义下面,
k
i
k^i
ki是有循环节的,
考虑这个循环节长度len的奇偶性,
如果是奇数,考虑连边的情况,
也就在第一个循环的结尾与第一个位置连边,
然后第二个位置跟第三个位置连边,
这样一来,就构成了一个环。
所以这种情况下的连通块个数n-(len/2)
如果是偶数,点是两两连边,所以这个情况下的联通块个数就是n-(len-1)。
现在问题就变为了如果求循环节。
因为模数是质数,根据费马小定理就可以知道,
k
m
o
−
1
k^{mo-1}
kmo−1同余1在模意义下。
假设
k
a
k^{a}
ka同余1在模意义下,而且a是最小正数满足这个式子。
显然
k
m
o
−
1
−
i
∗
a
k^{mo-1-i*a}
kmo−1−i∗a也是满足的,也就可以知道a一定是mo-1的约数,
于是,因为mo-1是一个固定的数,可以先预处理好mo-1的所有约数,
O
(
n
)
O(\sqrt n)
O(n)。
其实约数只有不到100个,非常少,
对于输入的每一个k,就可以直接从小到大枚举约数,判断一下就可以了。
code
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
char ch;
void read(int&n)
{
for(ch=getchar();ch<'0'|| ch>'9';ch=getchar());
for(n=0;'0'<=ch && ch<='9';ch=getchar())n=(n<<1)+(n<<3)+ch-48;
}
int T,n,m,x,k,s[103],ans;
ll ksm(ll x,int y)
{
ll s=1;
for(;y;y>>=1,x=x*x%n)
if(y&1)s=s*x%n;
return s;
}
int main()
{
n=998244352;
for(int i=1;i*i<=n;i++)
if(n%i==0)s[++s[0]]=i,s[++s[0]]=n/i;
sort(s+1,s+1+s[0]);
freopen("braid.in","r",stdin);
freopen("braid.out","w",stdout);
for(read(T);T;T--)
{
read(n);read(m);read(x);read(k);
if(k==1)printf("%d\n",n);else
{
for(int i=1;i<=s[0];i++)
if(ksm(k,s[i])==1)
{
ans=s[i];
break;
}
if(ans&1)ans--;else ans=ans>>1;
printf("%d\n",max(1,n-min(ans,m)));
}
}
}