Pass! (bsgs 推柿子)

请添加图片描述
题意:
有T组样例,一共n个人传球,从1号开始最后回到1号,给定方案数,求最小的传球次数。(t<=100,n<=1e6,方案数<=998244353)

思路:
考虑推柿子 。 f[i]代表从1号开始传了i次回到1号的方案数,
f[i]=(n-2)*f[i-1]+(n-1)*f[i-2],这里考虑两种情况*,第i-1个人如果是1号人员和不是1号人员,如果是一号人员,那么情况就是第i个人选择有(n-1)种,除了1号 。 还有一种如果不是一号,那么第i个人有(n-2)种选法(和左右两边的人都不相等)。然后通过特征方程来求得
f(t)=((n-1)^t+(n-1)*(-1)^t)/n=x (mod 998244353).通过移项变成指数形式,可恶的是这个(-1)的n次的怎么处理呢,这里我们可以分成两种情况,一种是奇数,一种是偶数,公差都是原来a的平方,循环节还是同一个。记录两个的最小值最后取min就行了。ps:杭电卡hashmap,这里我们手写两个hashmap,能快将近8,9倍。*
O(T*sqrt(n)*log(n))
code:

#include<iostream>
#include<cmath>
#include<unordered_map>
#include<cstring>
using namespace std;
const int mod=998244353;
typedef long long ll;
const int  N=100007;
//#define int long long
int mp2[N],mp[N];
int mpans[N],mpans2[N];
int qmi(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)	res=1ll*res*a%mod;
		b>>=1;
		a=1ll*a*a%mod;
	}
	return res;
}
int find(int h[],int x)
{
	int d=(x%N+N)%N;
	while(h[d]!=0x3f3f3f3f && h[d]!=x)
	{
		d++;
		if(d==N)
			d=0;
	}
	return d;
}
int bsgs(int a,int b,int p)
{
	// unordered_map<int,int>mp,mp2;
	int f1=(b-1+mod)%mod;//奇数
	int f2=(b+1)%mod;//偶数
	// cout<<f1<<" "<<f2<<endl;
	int k=sqrt(p)+1;
	int kt= 1ll*a*a%mod;
	int ak=1;
//	cout<<"k="<<k<<endl;
	for(int i=0,j1=1ll*f1*a%mod,j2=f2;i<=k-1;i++)
	{
		int t;
		mp[t=find(mp,j1)]=j1;
		mpans[t]=i;
		j1=1ll*j1*kt%mod;
		mp2[t=find(mp2,j2)]=j2;
		mpans2[t]=i;
		j2=1ll*j2*kt%mod;
		ak=1ll*ak*kt%mod;
		//cout<<j1<<" "<<j2<<endl;
	}
	int ans=2e9;
	for(int i=1,j=ak;i<=k;i++)
	{
		//cout<<i*k<<endl;
		int t;
		if(mp[t=find(mp,j)]==j)	ans=min(2*(i*k-mpans[t=find(mp,j)]),ans);
		if(mp2[t=find(mp2,j)]==j) ans=min(ans,2*(i*k-mpans2[t=find(mp2,j)])+1);
		j=1ll*j*ak%mod;


	}
	if(ans==2e9)	return -1;
	return ans;

}
signed main()
{
	ios::sync_with_stdio(0);
	int t;

	cin>>t;
	while(t--)
	{
		memset(mp,0x3f,sizeof mp);
		memset(mp2,0x3f,sizeof mp2);
		int n,x;
		cin >> n >> x;
		if(x==1)
		{
			cout<<0<<endl;
			continue;	
		}
		if(x==0)
		{
			cout<<1<<endl;
			continue;
		}
		x=1ll*n*x%mod*qmi(n-1,mod-2)%mod;
		cout<<bsgs((n-1+mod)%mod,x,mod)<<endl;
	}


	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值