题目链接
题目大意:
现在有N个人,传球,一开始球在1号手中,每秒都要传递一次球,现在告诉我们传球t秒后回到一号手中的方案数%mod的值为X,求解最小的时间t满足上述,若无解,输出-1 。
解题思路:
首先要推敲一下对于N,不同时间的时候的合法方案数的数量。
我是画了一个二叉树观察的。
显然t秒内传球方案数是(N-1)t 但是有不合法的方案数,因为最终必须回到一号。根据画的二叉树,手算发现规律如下。
t为奇数时 :
合法方案数sum=Σ(j=2;j<=t;j=j+2) (n-1)J - Σ(i=1;i<n;i=i+2) (n-1)i
t为偶数时:
合法方案数sum=Σ(j=1;j<=t;j=j+2) (n-1)J - Σ(i=2;i<n;i=i+2) (n-1)i
利用等比化简得到
sum=[(n-1)t + (-1)t (n-1) ] /n %mod=X%mod
那么不妨把分母约掉,常数移项。
等价于求解 (n-1)t = m的最小正整数t的解,BSGS算法即可
注意对于奇数的解ans必须也是奇数,同理偶数。
ps: 比赛的时候跑了800/2000 ms ((听说比赛的时候OJ跑的快些?
ps:填坑,比赛的代码(exBSGS)赛后TLE了。换成BSGS改成unmap就过了
#include<bits/stdc++.h>
#include <map>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <unordered_map>
#define ll long long
#define int ll
using namespace std ;
unordered_map<ll, int> H ;
int N, M, P,ans ; // N ^x = M (mod P)
//const int P=998244353;
inline ll gcd(ll a, ll b)
{
if (!b)
return a ;
return gcd(b, a % b) ;
}
inline ll expow(ll a, ll b, ll mod)
{
ll res = 1 ;
while (b)
res = ((b & 1)?res * a % mod : res), a = a * a % mod, b >>= 1 ;
return res ;
}
inline ll exgcd(ll &x, ll &y, ll a, ll b)
{
if (!b)
{
x = 1, y = 0 ;
return a ;
}
ll t = exgcd(y, x, b, a % b) ;
y -= x * (a / b) ;
return t ;
}
inline ll BSGS(ll a, ll b, ll mod, ll qaq)
{
H.clear() ;
ll Q, p = ceil(sqrt(mod)), x, y ;
exgcd(x, y, qaq, mod), b = (b * x % mod + mod) % mod,
Q = expow(a, p, mod), exgcd(x, y, Q, mod), Q = (x % mod + mod) % mod ;
for (ll i = 1, j = 0 ; j <= p ; ++ j, i = i * a % mod)
if (!H.count(i))
H[i] = j ;
for (ll i = b, j = 0 ; j <= p ; ++ j, i = i * Q % mod)
if (H[i])
return j * p + H[i] ;
return -1 ;
}
inline ll exBSGS()
{
ll qaq = 1 ;
ll k = 0, qwq = 1 ;
if (M == 1)
return 0 ;
while ((qwq = gcd(N, P)) > 1)
{
if (M % qwq)
return -1 ;
++ k, M /= qwq, P /= qwq, qaq = qaq * (N / qwq) % P ;
if (qaq == M)
return k ;
}
return (qwq = BSGS(N, M, P, qaq)) == -1 ? -1 : qwq + k ;
}
//N^x =m%p
signed main()
{
ll t;
scanf("%lld",&t);
while(t--)
{
ll rnmgb=9999999999999999;
P=998244353;
ans=0;
ll no=0;
ll lcj;
ll nm;
scanf("%lld%lld",&N,&lcj);
nm=N;
ll lcj1=((lcj*N)%P+(N-1)+P)%P; // t奇数
ll lcj2=((lcj*N)%P-(N-1)+P)%P; // 偶数
M=lcj1;
N--;
N %= P, M %= P, ans = exBSGS() ;
if(ans<0||ans%2!=1)
{
no++;
}
else
{
rnmgb=min(rnmgb,ans);
}
P=998244353;
ans=0;
N=nm-1;
M=lcj2;
N %= P, M %= P, ans = exBSGS() ;
if(ans<0||ans%2!=0)
{
no++;
}
else
{
rnmgb=min(rnmgb,ans);
}
if(no==2)
{
printf("-1\n");
continue;
}
printf("%lld\n",rnmgb);
}
}
BSGS
真想手写hash_map
#include<bits/stdc++.h>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<map>
#define LL long long
#define Re LL
using namespace std;
const int N=1e5+3;
int x,y,m,P;
unordered_map<LL,LL>B;
inline void in(Re &x)
{
int f=0;
x=0;
char c=getchar();
while(c<'0'||c>'9')
f|=c=='-',c=getchar();
while(c>='0'&&c<='9')
x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
//x^k = y (mod P)
//k = am-b (m=sqrt(P)+1, a\in[1,m], b\in[0,m-1])
//(x^m)^a = y x^b (mod P)
inline int mi(Re x,Re k)
{
Re s=1;
while(k)
{
if(k&1)
s=(LL)s*x%P;
x=(LL)x*x%P,k>>=1;
}
return s%P;
}
int main()
{
P=998244353;
Re tt;
in(tt);
while(tt--)
{
Re lcj,rnm;
in(lcj);
in(rnm);
Re lcj1=((lcj*rnm)%P+(lcj-1)+P)%P; // t奇数
Re lcj2=((lcj*rnm)%P-(lcj-1)+P)%P; // 偶数
if(lcj2==1)
{
printf("0\n");
continue;
}
x=lcj-1;
y=lcj1;
m=sqrt(P)+1;
Re okk=0;
Re daan=-1;
for(Re b=0,s=y; b<m; ++b)
B[s]=b,s=(LL)s*x%P;
Re tmp=mi(x,m)%P;
for(Re a=1,s=1; a<=m; ++a)
{
s=(LL)s*tmp%P;
if(B.count(s)&&(a*m-B[s])%2==1)
{
if(daan==-1)
{
daan=a*m-B[s];
}
okk++;
break;
}
}
B.clear();
x=lcj-1;
y=lcj2;
m=sqrt(P)+1;
// Re okk=0;
for(Re b=0,s=y; b<m; ++b)
B[s]=b,s=(LL)s*x%P;
tmp=mi(x,m)%P;
for(Re a=1,s=1; a<=m; ++a)
{
s=(LL)s*tmp%P;
if(B.count(s)&&(a*m-B[s])%2==0)
{
if(daan==-1)
{
daan=a*m-B[s];
}
else
daan=min(daan,a*m-B[s]);
okk++;
break;
}
}
B.clear();
if(!okk)
printf("-1\n");//continue;
else
printf("%lld\n",daan);
}
}