——综合
原题传送门
Description
无
Data
数据
提取码:2cs9
嚣张到给你数据
Tip:下表提示程序可能要实现的功能:
测试点 | 功能编号 |
---|---|
1~3 | 1_998244353 |
4 | 1? |
5 | 1?+ |
6,7 | 1wa_998244353 |
8~10 | 2p |
11~13 | 2u |
14,15 | 2g |
16 | 2g+ |
功能编号越接近,实现的功能越接近.
前言
肝了一天才做了出来. ?
第一眼看过去:(O_o)??什么鬼题目?
言归正传:
1.强烈建议本题用文件输入输出,否则你的命令行会很高能.
2.为了更好的被nue体验,本题一次一个样例的做.
//代码中的 ll 为 long long
思路
这是综合题目.
说好了是综合,说不好就是"大杂烩".
什么都有.
1
下载数据看了一下,嘿!找规律来着
第一个数据的输出强烈暗示:
//这里因为文件太大不放了
这分明就是
1
9
x
19^x
19x!
但是看着后面不对,应该不是高精度.
那就应该是取模!
模数是什么?
正想着,回头看题:
1
_
998244353
1\_998244353
1_998244353
有趣,这个后缀是什么?
盯了十秒钟发现,这就是著名的质数
998244353
998244353
998244353!
对它取模,快速幂完事.
2
看到第二个数据慌了:这到底是不是
l
o
n
g
l
o
n
g
long\ long
long long能解决的?
仔细数了数位数,发现都在
1
0
18
10^{18}
1018以下,
l
o
n
g
l
o
n
g
long\ long
long long是可以应对的.
只不过这次还要写一个快速乘,以防爆掉.
3
看到第三个数据彻底慌了.
不由惊呼:这是个啥?
莫非还是想让我写高精度?
lazy的我当然不想写高精度,于是分析:真的需要吗?
在脑海里翻翻有什么能够降低指数的公式,结果翻到了这个:
a,n互质: a b ≡ a b m o d    φ ( n ) ( m o d n ) a^b\equiv a^{b\mod \varphi(n)}\pmod n ab≡abmodφ(n)(modn)
和
a,n不一定互质,b > \gt >n
a b ≡ a b m o d    φ ( n ) + φ ( n ) ( m o d n ) a^b\equiv a^{b\mod \varphi(n)+\varphi(n)}\pmod n ab≡abmodφ(n)+φ(n)(modn)
优先采用第一个.
由于
998244353
998244353
998244353是个质数,所以
φ
(
998244353
)
=
998244352
\varphi(998244353)=998244352
φ(998244353)=998244352.
所以,带个快读就又很快活了.
ll read(ll m)//m 是模数
{
ll w=1,x=0;
char c=getchar();
while (!isdigit(c))
{
if (c=='-') w=-1;
c=getchar();
}
while (isdigit(c))
{
x=(x*10+c-'0')%(m-1);
c=getchar();
}
return w*x;
}
4
第四个数据,输出不一样了?
都变成六七位数是怎么回事?
aiya!取模数变了!
还有这个
1
?
1?
1?是什么鬼?
显然是要我们自行寻找模数.
这可如何是好?
瞅一眼输出,大致范围都在
7
∗
1
0
5
7*10^5
7∗105左右,最大值在
1.1
∗
1
0
6
1.1*10^6
1.1∗106左右.
于是,我们写个暴力测一下
1.1
∗
1
0
6
1.1*10^6
1.1∗106到
1.2
∗
1
0
6
1.2*10^6
1.2∗106有哪个数符合条件.
你不会傻傻地把所有数据都测了吧
当然,我们只用第一个数
627...322
−
642666
627...322-642666
627...322−642666就行了.
别用0-1
约几秒的等待,找到了一个数1145141.
经过检验,得到
φ
(
1145141
)
=
1145140
\varphi(1145141)=1145140
φ(1145141)=1145140
于是,完美结束.
5
1
?
1?
1?变成了
1
?
+
1?+
1?+.
本来以为没什么大碍的,看了输出:aiya!取模数变大了!
感觉这个数连
l
o
n
g
l
o
n
g
long\ long
long long都装不下了.
于是,我们一边祈祷出题人的良心这个数在
l
o
n
g
l
o
n
g
long\ long
long long 之内,一边写暴力.
这种数据范围,暴力测试时间可能以年计.
于是,我们去找找规律.
很显然,如果能找到
x
<
y
x\lt y
x<y且
f
(
x
)
>
f
(
y
)
f(x)\gt f(y)
f(x)>f(y),就可以作为突破口.
具体说,这个模数就是
f
(
x
)
∗
1
9
y
−
x
−
f
(
y
)
f(x)*19^{y-x}-f(y)
f(x)∗19y−x−f(y)的因数.
为了简便计算量,我们让
y
−
x
y-x
y−x最小.
在Sample 5写了代码求得这样的x,y(代码就不写了 )
x
=
264708066
f
(
x
)
=
1996649514996338529
x=264708066\ \ \ \ f(x)=1996649514996338529
x=264708066 f(x)=1996649514996338529
y
=
264708068
f
(
y
)
=
1589589654696467295
y=264708068\ \ \ \ f(y)=1589589654696467295
y=264708068 f(y)=1589589654696467295
求出这个鬼畜的模数就是
719200885258981741674
719200885258981741674
719200885258981741674的因数.
虽然经过一波操作,但是这个数仍然爆出
l
o
n
g
l
o
n
g
long\ long
long long的范围.
然后我们在long long范围内经过质因数分解.
//或者Python也可以heiheihei
得到了这样一个诡异的数据:
5211500658258874318
5211500658258874318
5211500658258874318
果然出题人还是有良心的
这个数在
l
o
n
g
l
o
n
g
long\ long
long long以内.
所以,我们又很快活.
但建议还是用
u
n
s
i
g
n
e
d
l
o
n
g
l
o
n
g
unsigned\ long\ long
unsigned long long,因为感觉太容易爆了.
6
这倒是回归正题了,可是这个wa是什么意思?错误答案?
再看看输出:有负数!
可能是直接乘,然后溢出了…
所以这个不能用快速幂刷了…
直接写个暴力即可.
什么?你TLE了?
是这样写的:
int a[1000005];//注意是int
a[0]=1;
for (int i=1;i<=100000;i++)
a[i]=a[i-1]*19%998244353;
可别一个一个做了.
7
这个暴力就有些困难了,直接变态到
(
2
)
(2)
(2)的难度(没到
(
3
)
(3)
(3)真是幸运 )
但是想想,int范围也就是
2
32
2^{32}
232个数这还不够多吗.
也许,这些(错误的)
1
9
x
19^x
19x只能取到有限个值?
所以,我们尝试寻找循环节.
结果发现,还真是比较小:
S
t
a
r
t
=
55245
C
y
c
l
e
=
45699
Start=55245\ \ \ \ Cycle=45699
Start=55245 Cycle=45699
所以我们直接写出就可以了.
//当然别忘了预处理
printf("%lld\n",map[(a>Start?((a-Start)%Cycle+Start):a)]);
8
看到开头是2,就想到寻找
1
9
x
19^x
19x已经结束了.
变成了
2
p
2p
2p,说明要找
p
p
p.
可是
p
p
p是什么?
仔细看看数据,这个
p
p
p原来是
p
r
i
m
e
prime
prime!
这是要找质数.
对于质数输出’p’,对于合数输出’.’.
直接写一个欧拉筛即可.
9
没有’+’,说明并不变态…
错!左右端点变大了.
但是再看看,区间大小没有变,都是在
1
0
6
10^6
106左右.
所以只能一个一个做了.
但是直接试除是
O
(
L
N
)
O(L\sqrt N)
O(LN)的,还是会TLE.
所以没办法,只能请出判断质数大杀器——Miller_rabbin了.
const int base[5]={2,3,7,61,24251};
bool miller_rabbin(ll n)
{
if (n==2||n==3||n==7||n==61||n==24251) return true;
if (n==46856248255981ll||n<2||!(n&1)) return false;
ll d=n-1;
while (!(d&1)) d>>=1;
for (int i=0;i<5;i++)
{
ll t=qpow(base[i],d,n),k=d;//qpow是快速幂
for(;k!=n-1&&t!=n-1&&t!=1;k<<=1) t=(__int128)t*t%n;
if (t!=n-1&&(k&1)!=1) return false;
}
return true;
}
我们用
2
,
3
,
7
,
61
,
24251
2,3,7,61,24251
2,3,7,61,24251判断即可.
这样复杂度一下子被简化到
O
(
L
l
o
g
N
)
O(LlogN)
O(LlogN)了,是可以过的.
10
看到第十个数据,还是一样的套路!
直接套9的Miller_rabbin即可.
但数据有点大,小心别爆
l
o
n
g
l
o
n
g
long long
longlong.
11
从
2
p
2p
2p变成了
2
u
2u
2u,说明不是再找质数了.
翻开输出看看,有
0
,
+
,
−
…
0,+,-\dots
0,+,−…
这是什么诡异的东西?
再次在脑海里翻翻有什么东西能同时取到
0
,
+
,
−
0,+,-
0,+,−的值.
找到了一个可能有点关系的——莫比乌斯函数.
设 x = ∏ i = 1 n p i k i x=\prod_{i=1}^n{p_i^{k_i}} x=∏i=1npiki,其中 p i p_i pi为质数且互不相同,则:
μ ( x ) = { 1 x = 1 0 1 ⩽ i ⩽ n , k i > 1 ( − 1 ) n o t h e r s \mu(x)= \begin{cases} 1&x=1\\ 0&1\leqslant i \leqslant n,k_i>1\\ (-1)^n&others \end{cases} μ(x)=⎩⎪⎨⎪⎧10(−1)nx=11⩽i⩽n,ki>1others
莫比乌斯函数在小范围可以通过欧拉筛实现:
void Sieve(int x)
{
int cnt=0;
notprime[1]=true,mu[1]=1;
for (int i=2;i<=x;i++)
{
if (!notprime[i]) prime[++cnt]=i,mu[i]=-1;
for (int j=1;j<=cnt&&i*prime[j]<=x;j++)
{
notprime[prime[j]*i]=true;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
else mu[i*prime[j]]=-mu[i];
}
}
}
顺带实现了欧拉筛.
完美解决 ?
12
数据变大了.(有没有发觉出题人在偷懒 )
这时候就不是筛子能解决的了.
暴力判断?显然会TLE.
只有利用原有数据偷懒了.
既然数据不超过
1
0
12
10^{12}
1012,那么我们可以:
- 筛去 p i < 1 0 6 p_i<10^6 pi<106,同时统计 μ \mu μ.
- 剩下的要么是 1 1 1,要么是个 > 1 0 6 >10^6 >106的质数,判断返回即可.
不要一个一个数的筛,每次选定一个质数筛.
13
数据变得更大了,范围在
1
0
18
10^{18}
1018左右.
照葫芦画瓢,把它分成三部分.
- 暴力筛去所有 < 1 0 6 <10^6 <106的质数,同时统计 μ \mu μ.
- 剩下的数要么是一个大质数,要么是一个完全平方数,要么是两个不同质数的积.
对于这个剩下的大质数,我们:
- 先进行Miller_rabbin,如果是质数返回 − μ -\mu −μ,
- 然后判断是否为完全平方数,代码如下:
ll k=(ll)sqrt(a[i]);
if (k*k==a[i]) mu=0;
- 否则就是两个大质数的乘积,不会影响结果,返回
μ
\mu
μ.
这部分就被完美解决了.
//听说这里有人用 Pollard-Rho,TLE了一批…
14
数据变成
2
g
2g
2g了,这次是求什么?
打开输入一看,多了一个数
998244353
998244353
998244353,看来应该是模数.
但是看输出又觉得不对,这和取模
…
\dots
…好像没什么关系?
看来’g’标出的应该是特殊的,要求的数(显而易见)
突然,我看到了
3
3
3这里被打上了’g’.
突然间想到了刚学过的原根!’
998244353
998244353
998244353有一个原根
3
3
3.
所以,我们写个暴力就可以过.
不会的可以学习NTT.
pri[1]=2,pri[2]=7,pri[3]=17;
for (ll j=l;j<=r;j++)
{
is=true;
for (int k=1;k<=3;k++)
if (qpow(j,(mod-1)/pri[k],mod)==1)
{
is=false;
break;
}
if (is) putchar('g');
else putchar('.');
}
15
数据规模发生变化,并且多了一个模数
13123111
13123111
13123111.
没有预备的原根了.
但是,我们有定理:
设 φ ( p ) = ∏ i = 1 n p i k i \varphi(p)=\prod_{i=1}^n{p_i^{k_i}} φ(p)=∏i=1npiki,其中 p i p_i pi为质数且互不相同,若对于所有 p i p_i pi,有:
a φ ( p ) p i ≠ 1 a^{\frac{\varphi(p)}{p_i}}\ \ \neq 1 apiφ(p) ̸=1
那么a即为模p的原根.
所以,我们暴力枚举即可.
当然,暴力是不行的.
还有另外一种方法:
若已知有一原根 g g g,那么对于所有 i i i,若有:
g c d ( i , φ ( p ) ) = 1 gcd(i,\varphi(p))=1 gcd(i,φ(p))=1
则 g i g^i gi就是原根.
根据这个,就可以快乐的写原根了. ?
16
这一次…
???这个‘?’是什么意思?
看来是要自己推模数.
没有办法,只有暴力.
先取出
1
0
9
−
2
∗
1
0
9
10^9-2*10^9
109−2∗109的质数,再根据输出判定模数.
这个过程会很长…
最后判出模数为
1515343657
1515343657
1515343657.
至此本题完美结束个什么.
脏 · 套路
为什么这样说呢?
因为如果你拿着这样的代码提交,至少会有两个TLE.
原因就是——卡常.
这也太脏了吧…
所以,我们需要一波脏优化.
1
首先会TLE的应该会在11~13那一部分(本人第一次后两个点TLE了).
话说Mobius竟然会出问题?说好的
O
(
L
l
o
g
N
)
O(LlogN)
O(LlogN)呢?
本人的回答是:
“是的,但你忽略了常数”.
的确,如果直接套上面
2
p
2p
2p的Miller_rabbin判断,时间复杂度应该是
O
(
5
L
l
o
g
N
)
O(5LlogN)
O(5LlogN).
既然这个TLE了,我们不如…
减少几个凭证.
凭证是什么?说白了就是代入的数据.
这其实也是很冒险的事情,因为Miller_rabbin正是基于凭证的个数来保证准确性的.
所以,看删去哪几个凭证就需要对答案的耐心…
后来我们震惊的发现:只用2,3就可以过!
不由唾弃出题人
所以复杂度降至
O
(
2
L
l
o
g
N
)
O(2LlogN)
O(2LlogN),就全AC了.
2
接下来出现TLE的就是后面的
2
g
2g
2g(本人第一次也是后两个点TLE了).
这一部分和前面不挂钩,如何是好?
先瞅一眼14,15,16.惊奇的发现:
出题人又在偷懒!
那这样子我们也偷懒一波,先把前两个数据打下来…
puts(".g");
puts(".g.gg...g");
scanf("%lld%lld%lld%lld%lld%lld",&laji,&laji,&laji,&laji,&laji,&laji,&laji);
//laji表示...
嘿嘿嘿,这样子直接就
T
e
s
t
c
a
s
e
s
−
2
Testcases-2
Testcases−2了 ?
但是这样子时间还是不够优越,仍然存在TLE的可能.
所以这个时候,我们就要看看前面的算法了:
//首先说一句,g()和 g?()要分开写,不然还会TLE.
对于第十五个数据,我们先把
φ
(
13123111
)
\varphi(13123111)
φ(13123111)分解,然后找到各项质因数(很多).
然后我们打表改循环,预先就把有用的
i
i
i值求出来:
for(int i=2;i<=13123111;i+=2) vis[i]|=1;
for(int i=3;i<=13123111;i+=3) vis[i]|=1;
for(int i=5;i<=13123111;i+=5) vis[i]|=1;
for(int i=7;i<=13123111;i+=7) vis[i]|=1;
for(int i=11;i<=13123111;i+=11) vis[i]|=1;
for(int i=13;i<=13123111;i+=13) vis[i]|=1;
for(int i=19;i<=13123111;i+=19) vis[i]|=1;
for(int i=23;i<=13123111;i+=23) vis[i]|=1;//求指标
for(int i=6,j=1;;i=(int)(6LL*i%13123111),++j)
{
if(a[i]) break;
a[i]=j;
}//找值
这样T15就可以AC了.
对于第十六个数据,只有一句话:
φ
(
1515343657
)
\varphi(1515343657)
φ(1515343657)的质因数只有
2
,
3
,
4003
,
15773
2,3,4003,15773
2,3,4003,15773.
仿照T14的做法,只不过需要判定的数多了一个.
应该不会TLE吧…
如果不行,可以稍微打一个小表…主要是这里打表才是正解…
Code
这里是代码实现.
没有注释(吗?),有疑问的可以看上文和其他奆老代码.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int base[5]={2,3};
int cnt;
bool isprime[1000010],vis[20000010];
ll mu[1000010],a[20000010],prime[1000010];
ll qmul(ll a,ll b,ll p)
{
a%=p,b%=p;
ll c=(long double) a*b/p;
ll ans=a*b-c*p;
if (ans<0) ans+=p;
else if (ans>=p) ans-=p;
return ans;
}
ll qpow(ll a,ll b,ll p)
{
ll ans=1;
while (b)
{
if (b&1) ans=qmul(ans,a,p);
b>>=1;
a=qmul(a,a,p);
}
return ans;
}
ll read(ll m)
{
ll w=1,x=0;
char c=getchar();
while (!isdigit(c))
{
if (c=='-') w=-1;
c=getchar();
}
while (isdigit(c))
{
x=(x*10+c-'0')%(m-1);
c=getchar();
}
return w*x;
}
ll solve(ll k)
{
ll a=read(k);
return qpow(19,a,k);
}
void wasolve()
{
const int S=55246,C=45699;
int biao[S+C+5]={0};
biao[0]=1;
for (int i=1;i<=S+C;i++) biao[i]=biao[i-1]*19%998244353;
ll a,n;
scanf("%lld",&n);
for (int i=1;i<=n;i++)
{
scanf("%lld",&a);
printf("%d\n",biao[(a<=S)?a:(a-S)%C+S]);
}
}
bool miller_rabbin(ll n,int in)
{
if (n==2||n==3||n==7||n==61||n==24251) return true;
if (n==46856248255981ll||n<2||!(n&1)) return false;
ll d=n-1;
while (!(d&1)) d>>=1;
for (int i=0;i<((in==1)?2:1);i++)
{
ll t=qpow(base[i],d,n),k=d;
for(;k!=n-1&&t!=n-1&&t!=1;k<<=1) t=(__int128)t*t%n;
if (t!=n-1&&(k&1)!=1) return false;
}
return true;
}
void Mu()
{
ll T,l,r;
scanf("%lld",&T);
cnt=0;
while (T--)
{
scanf("%lld%lld",&l,&r);
ll L=r-l+1;
for (int i=1;i<=L;i++)
a[i]=l+i-1,mu[i]=1,isprime[i]=true;
for (int i=2;i<=L;i++)
{
if (isprime[i]==true)
{
prime[++cnt]=i;
for (ll j=(l+i-1)/i*i;j<=r;j+=i)
{
ll k=j-l+1;
if ((a[k]/i)%i==0) mu[k]=0;
else mu[k]*=-1;
while (a[k]%i==0) a[k]/=i;
}
}
for (int j=1;j<=cnt&&i*prime[j]<=L;j++)
{
isprime[i*prime[j]]=false;
if (i%prime[j]==0) break;
}
}
for (int i=1;i<=L;i++)
{
if (a[i]==1||mu[i]==0) continue;
if (miller_rabbin(a[i],2)) mu[i]=-mu[i];
else
{
ll k=(ll)sqrt(a[i]);
if (k*k==a[i]) mu[i]=0;
}
}
for (int i=1;i<=L;i++)
{
if (mu[i]==0) putchar('0');
else if (mu[i]==1) putchar('+');
else putchar('-');
}
printf("\n");
}
}
ll gcd(ll a,ll b)
{
return (b==0)?a:gcd(b,a%b);
}
void G()
{
ll pri[35],T,laji;
scanf("%lld",&T);
puts(".g");
puts(".g.gg...g");
scanf("%lld%lld%lld%lld%lld%lld",&laji,&laji,&laji,&laji,&laji,&laji,&laji);
cnt=0;
bool is;
T-=2;
while (T--)
{
ll l,r,g,mod;
scanf("%lld%lld",&l,&r);
scanf("%lld",&mod);
if (mod==13123111)
{
for(int i=2;i<=13123111;i+=2) vis[i]|=1;
for(int i=3;i<=13123111;i+=3) vis[i]|=1;
for(int i=5;i<=13123111;i+=5) vis[i]|=1;
for(int i=7;i<=13123111;i+=7) vis[i]|=1;
for(int i=11;i<=13123111;i+=11) vis[i]|=1;
for(int i=13;i<=13123111;i+=13) vis[i]|=1;
for(int i=19;i<=13123111;i+=19) vis[i]|=1;
for(int i=23;i<=13123111;i+=23) vis[i]|=1;
for(int i=6,j=1;;i=(int)(6LL*i%13123111),++j)
{
if(a[i]) break;
a[i]=j;
}
for(int i=1;i<13123111;i++) vis[a[i]]?putchar('.'):putchar('g');
}
else
{
cnt=3;
pri[1]=2,pri[2]=7,pri[3]=17;
for (ll j=l;j<=r;j++)
{
is=true;
for (int k=1;k<=cnt;k++)
if (qpow(j,(mod-1)/pri[k],mod)==1)
{
is=false;
break;
}
if (is) putchar('g');
else putchar('.');
}
}
puts("");
}
}
void GG()
{
ll pri[35],T,laji;
scanf("%lld",&laji);
puts(".g");
puts(".g.gg...g");
scanf("%lld%lld%lld%lld%lld%lld",&laji,&laji,&laji,&laji,&laji,&laji,&laji);
cnt=0;
cnt=4;
pri[1]=2,pri[2]=3,pri[3]=4003,pri[4]=15773;
ll mod=1515343657,l=233333333,r=234133333;
for (ll j=l;j<=r;j++)
{
bool is=true;
for (int k=1;k<=cnt;k++)
if (qpow(j,(mod-1)/pri[k],mod)==1)
{
is=false;
break;
}
if (is) putchar('g');
else putchar('.');
}
}
int main()
{
// freopen("software.in","r",stdin);
// freopen("software.out","w",stdout);
string op;
ll n,a,l,r;
cin>>op;
if (op=="1_998244353")
{
scanf("%lld",&n);
for (int i=1;i<=n;i++)
{
printf("%lld\n",solve(998244353));
}
}
else if (op=="1?")
{
scanf("%lld",&n);
for (int i=1;i<=n;i++)
{
printf("%lld\n",solve(1145141));
}
}
else if (op=="1?+")
{
scanf("%lld",&n);
for (int i=1;i<=n;i++)
{
printf("%lld\n",solve(5211600617818708273));
}
}
else if (op=="1wa_998244353")
{
wasolve();
}
else if (op=="2p")
{
scanf("%lld",&n);
for (int i=1;i<=n;i++)
{
scanf("%lld%lld",&l,&r);
for (ll i=l;i<=r;i++)
if (miller_rabbin(i,1)) putchar('p');
else putchar('.');
printf("\n");
}
}
else if (op=="2u")
{
Mu();
}
else if (op=="2g")
{
G();
}
else if (op=="2g?")
{
GG();//充分体现心情.
}
return 0;
}
后记
本蒟蒻的第一道省选题.
个人印象,省选都是肝出来的,会做的都是Dalao…
感谢奆老关注 qwq ?