1. 程式人生 > >JZOJ5946. 【NOIP2018模擬11.02】時空幻境(braid)

JZOJ5946. 【NOIP2018模擬11.02】時空幻境(braid)

Description

Tim擁有控制時間的能力。他學會了BFS後,出了一道題:求出一張無向圖中連通塊的個數。他想請你做出這道題來 在這裡插入圖片描述 在這裡插入圖片描述

題解

其實跟bfs是沒有關係的。 可以知道連邊的點的編號是xkix*k^i 而在模意義下面,kik^i是有迴圈節的, 考慮這個迴圈節長度len的奇偶性, 如果是奇數,考慮連邊的情況, 也就在第一個迴圈的結尾與第一個位置連邊, 然後第二個位置跟第三個位置連邊, 這樣一來,就構成了一個環。 所以這種情況下的連通塊個數n-(len/2) 如果是偶數,點是兩兩連邊,所以這個情況下的聯通塊個數就是n-(len-1)。 現在問題就變為了如果求迴圈節。 因為模數是質數,根據費馬小定理就可以知道, k

mo1k^{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)));
		}
	}
}