Description

Tim拥有控制时间的能力。他学会了BFS后,出了一道题:求出一张无向图中连通块的个数。他想请你做出这道题来
JZOJ5946. 【NOIP2018模拟11.02】时空幻境(braid)
JZOJ5946. 【NOIP2018模拟11.02】时空幻境(braid)

题解

其实跟bfs是没有关系的。
可以知道连边的点的编号是xkix*k^i
而在模意义下面,kik^i是有循环节的,
考虑这个循环节长度len的奇偶性,
如果是奇数,考虑连边的情况,
也就在第一个循环的结尾与第一个位置连边,
然后第二个位置跟第三个位置连边,
这样一来,就构成了一个环。
所以这种情况下的连通块个数n-(len/2)
如果是偶数,点是两两连边,所以这个情况下的联通块个数就是n-(len-1)。
现在问题就变为了如果求循环节。
因为模数是质数,根据费马小定理就可以知道,
kmo1k^{mo-1}同余1在模意义下。
假设kak^{a}同余1在模意义下,而且a是最小正数满足这个式子。
显然kmo1iak^{mo-1-i*a}也是满足的,也就可以知道a一定是mo-1的约数,
于是,因为mo-1是一个固定的数,可以先预处理好mo-1的所有约数,O(n)O(\sqrt 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)));
		}
	}
}

相关文章:

  • 2021-04-14
  • 2021-04-22
  • 2021-12-21
  • 2021-06-11
  • 2022-01-14
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2021-10-18
  • 2021-06-25
  • 2021-09-20
  • 2021-04-06
  • 2022-12-23
相关资源
相似解决方案