首页 > 其他分享 >数论超级缝合怪——P5285骗分过样例

数论超级缝合怪——P5285骗分过样例

时间:2022-10-13 21:01:13浏览次数:65  
标签:过样 骗分 Case int 质数 chker operatorname && P5285

P5285 [十二省联考 2019] 骗分过样例

\(\operatorname{Case} 1\sim7:\)

从\(ans[0]=1,ans[1]=19,ans[2]=361\)等可以看出是\(19^n\)

\(\quad\operatorname{Case} 1,2\)直接快速幂可过,\(\operatorname{Case} 3\)加个欧拉定理就行。 这里都有写到

\(\quad\operatorname{Case} 4,5\)都是要猜模数,一个\(naive\)的想法就是找到输出数据中的最大值之后开始枚举模数,可以很快找到\(\operatorname{Case} 4\)的模数。

虽然我\(\operatorname{Case}4\)在\(114514\)后面加个数就试出来了

\(\quad\operatorname{Case} 5\)就需要一点技巧了:

我选的方法如下:选择几对\((x,y) (x<y)\),并使得\(y-x\)尽可能小。

可以知道\(ans[y]-ans[x]\times 19^{y-x}\)为模数的倍数,所以用python算几组数的最大公约数大致可以得到模数。

\(\quad\operatorname{Case} 6,7:\)

众所周知,乘法不开long long会答案错误,而且有可能出现负数。

所以猜想这两组数据就是这样得到的,写个暴力就可以过\(\operatorname{Case} 6\)

\(\operatorname{Case} 7\)数据好大,而且快速幂的溢出是不对的(试试就知道了),怎么办呢?

这种可以由\(i\)推至\(i+1\)的可以试图找环,所以我们会想到用map来辅助找环。

经试验,会在第\(50000+\)项出现一个长度为\(50000+\)的环。

\(\operatorname{Case} 8\sim 10:(r-l\leq10^6)\)

根据小样例和输出的字母p可以推出这部分是要筛出\([l,r]\)内的质数。

这部分很简单,大数据直接用米勒来宾测试判断质数就行

\(\operatorname{Case} 11\sim 13:(r-l\leq10^6)\)

根据质数的地方都是-,有平方因子的都是0,字母u,可以猜测这部分是计算\([l,r]\)内的莫比乌斯函数

\(\quad\operatorname{Case} 11(l,r\leq10^6)\)直接筛,\(\operatorname{Case} 12(l,r\leq10^{12})\)是套路:筛出\(\sqrt{r}\)内的质数,枚举它们在区间\([l,r]\)的倍数,同时计算\(\mu\)

\(\quad\operatorname{Case} 13(l,r\leq10^{18})\)只能筛到\(\sqrt[3]{r}\),还是枚举它们在区间\([l,r]\)的倍数。

可以想到有一部分数会有\([10^6,10^{18}]\)内的质数因子(最多有两个),可以这么处理:

枚举倍数的同时对每个\(x\)算出它在\([10^6,10^{18}]\)内的质数因子的乘积\(y_x\)(\(x\)本身除掉\([1,10^6]\)的质数因子即可)。

枚举倍数后分三类讨论:

  1. \(y_x\)为完全平方数:\(\mu(x)\leftarrow0\)
  2. \(y_x\)为质数:\(\mu(x)\leftarrow-\mu(x)\)
  3. 以上两种都不是,这种情况\(y_x\)一定是两个不同质数的乘积(或者是\(1\)),不需要改\(\mu(x)\)的值

注意这部分可能被卡常。

\(\operatorname{Case} 14\sim 16:\)

众所周知:\(998244353\)最小的原根是\(3,998244353=1+2^{23}\times7\times17\)

根据这个和g可以推断出是算出区间\([l,r]\)内\(p\)的原根。

\(\quad\operatorname{Case} 14:\)质数\(p\)的原根\(x\)满足:\(\forall m\in \operatorname{prime},m|p-1,x^{\frac{p-1}{m}}\not\equiv1\pmod p\),用这个判断就行。

\(\quad\operatorname{Case} 15:\)计算\(13123111\)的所有原根,用\(O(n\log\log\log n)\)的计算方式即可。

\(\quad\operatorname{Case} 16:\)需要猜模数,我感觉没什么好的方法,就写了如下代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int MAXN=1000002;
bool np[MAXN];
int prime[MAXN],pcnt,mu[MAXN];
set<int>v;
const int p[]= {0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103},L=27;
inline ll mul(const ll&a,const ll&b,const ll&mod) {
	ll r=((long double)a*b/mod+0.5);
	r=a*b-r*mod;
	return r<0?r+mod:r;
}
inline ull mr_fastpow(ull a,ull k,const ull&bigmod) {
	ull base=1;
	for(; k; k>>=1,a=mul(a,a,bigmod)) if(k&1)base=mul(base,a,bigmod);
	return base;
}
inline bool mr(const ll n,const ll a) {
	ll t=n-1;
	while(!(t&1)) t>>=1;
	ll buf=mr_fastpow(a,t,n);
	if(buf==1||buf==n-1) return true;
	while((t<<=1)<n-1) {
		buf=mul(buf,buf,n);
		if(buf==n-1) return true;
	}
	return false;
}
inline bool ptest(ll x) {
	if(x==1) return false;
	for(int i=1; i<=L; ++i)
		if(!(x%p[i]))
			return x==p[i];
	return x<=10000ll?true:mr(x,2)&&mr(x,67)&&mr(x,233);
}
inline int fastpow(int a,int k,int mod) {
	int base=1;
	for(; k; k>>=1,a=a*1ll*a%mod) if(k&1)base=base*1ll*a%mod;
	return base;
}
inline bool chker(int a,int P) {
	int t=P-1;
	for(int i=2; i*i<=t; ++i)
		if(!(t%i)) {
			t/=i;
			if(fastpow(a,P/i,P)==1) return false;
			while(!(t%i))t/=i;
		}
	if(t>1)if(fastpow(a,P/t,P)==1) return false;
	return true;
}
//以上为米勒来宾质数测试
inline bool Chk(int x) {//判断x是否是质数且满足前11个数
	return ptest(x)
	       &&!chker(233333333,x)
	       &&!chker(233333334,x)
	       &&!chker(233333335,x)
	       && chker(233333336,x)
	       && chker(233333337,x)
	       && chker(233333338,x)
	       &&!chker(233333339,x)
	       &&!chker(233333340,x)
	       && chker(233333341,x)
	       && chker(233333342,x)
	       &&!chker(233333343,x);
}
inline bool Chk2(int x) {//判断x是否满足后20个数
	for(int i=234133333,cnt=1; cnt<=20; ++cnt,--i)
		if(chker(i,x)!=(cnt==3||cnt==8||cnt==10||cnt>18))
			return false;
	return true;
}
int a[100005],n;
int main() {
	freopen("chk.txt","r",stdin);
	freopen("chk.txt","w",stdout);
	int l=1e9+1,r=2e9-1;
	int cnt=0;
	for(int i=l; i<=r; i+=2)//暴力枚举模数
		if(Chk(i)&&Chk2(i))cout<<i<<endl;
	return 0;
}

大约速通一次MC的时间(\(20\sim40\min\))就跑出来是\(1515343657 \),用\(\operatorname{Case} 14\)的方法就做完了

代码也就有亿点难写。

标签:过样,骗分,Case,int,质数,chker,operatorname,&&,P5285
From: https://www.cnblogs.com/mod998244353/p/16789631.html

相关文章