本博客主要记录了自己的刷题内容,因此每道题不会写太多,请谅解。
Part 1 数论提高题选做
CF1034A Enlarge GCD
给你n个数,去掉尽量少的数使得剩下数的gcd比原来的大。无解输出-1
若使gcd变大,我们只需要找到一个不是所有数都有的一个质因子,然后将没有这个质因子的这些数删去即可,因此我们直接统计每个质因子出现次数,取n−最小值n-最小值n−最小值即可。
时间复杂度:O(namax)O(n \sqrt{a_{max}})O(namax)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define MAXP 15000000
#define MAXN 300000
#define LL long long
bool isp[MAXP+5];
int a[MAXN+5];
int cnt[MAXP+5];
int prime[MAXN*10+5];
int Pnum=0;
int n,ans;
LL gcd(LL m,LL n)
{
while(m>0)
{LL c=n%m;n=m,m=c;}
return n;
}
void Prime(int Sl)
{
isp[1]=1;
for(int i=2;i<=Sl;i++)
{
if(!isp[i])prime[Pnum++]=i;
for(int j=0;j<Pnum&&prime[j]*i<=Sl;j++)
{
isp[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
Prime(MAXP);
int G=a[1];
for(int i=2;i<=n;i++)G=gcd(G,a[i]);
for(int i=1;i<=n;i++)
{
a[i]/=G;
for(int j=0;a[i]>=prime[j]*prime[j];j++)
{
if(a[i]%prime[j]==0)cnt[prime[j]]++;
while(a[i]%prime[j]==0)a[i]/=prime[j];
}
if(a[i]!=0)cnt[a[i]]++;
}
ans=n;
for(int j=0;j<Pnum;j++)
ans=min(ans,n-cnt[prime[j]]);
printf("%d",ans==n? -1:ans);
}
CF906D Power Tower
给定一个数列w1,w2,...,wnw_1,w_2,...,w_nw1,w2,...,wn和模数ppp,每次询问一个区间[l,r][l,r][l,r],求wlwl+1wl+2...wrmod pw_l^{w_{l+1}^{w_{l+2}^{{...}^{w_r}}}} \mod pwlwl+1wl+2...wrmodp的值
一道很玄学的题目。
首先ppp不是质数,因此我们无法使用欧拉定理。
然而有一个神奇的扩展欧拉定理
当b≥ϕ(p)时,有ab≡abmod ϕ(p)+ϕ(p)当 b≥\phi(p) 时,有a^b≡a^{b \mod \phi(p)+\phi(p)}当b≥ϕ(p)时,有ab≡abmodϕ(p)+ϕ(p)。
因此我们在取模时直接加上一个ϕ(p)\phi(p)ϕ(p)即可。
然后我们直接暴力递归计算。
然后它就过了…
这是因为ϕ(ϕ(ϕ(...ϕ(p))))\phi(\phi(\phi(...\phi(p))))ϕ(ϕ(ϕ(...ϕ(p))))很快就会变为111,大概时O(logp)O(\log p)O(logp)级别,这时候退出即可。
#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
#include<cmath>
using namespace std;
#define MAXN 100000
#define LL long long
int read(){
int x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return x*F;
}
int n,MOD,a[MAXN+1],Q;
map <int,int> phi;
LL pMOD(LL x,LL p){return x<p?x:(p+x%p);}
int Phi(int x){
int res=x,ps=x;
if(phi.count(x))return phi[x];
for(int i=2;i<=sqrt(x);i++)
if(x%i==0){
res=res/i*(i-1);
while(x%i==0)x/=i;
}
if(x>1)res=res/x*(x-1);
return phi[ps]=res;
}
int Qpow(int a,int b,int p){
int res=1;
while(b){
if(b&1)res=pMOD(1LL*res*a,p);
a=pMOD(1LL*a*a,p);
b>>=1;
}return res;
}
int get_ans(int l,int r,int p){
if(l==r||p==1)return pMOD(a[l],p);
return Qpow(a[l],get_ans(l+1,r,Phi(p)),p);
}
int main()
{
n=read(),MOD=read();
for(int i=1;i<=n;i++)a[i]=read();
Q=read();
while(Q--){
int l=read(),r=read();
printf("%d\n",get_ans(l,r,MOD)%MOD);
}
}
CF17D Notepad
求(b−1)bn−1 mod c(b-1)b^{n-1} \bmod c(b−1)bn−1modc。
b≤10106,n≤10106,c≤109b \leq 10^{10^6},n \leq 10^{10^6},c \leq 10^9b≤10106,n≤10106,c≤109
运用上一题的扩展欧拉定理直接快速幂计算即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 1000000
#define LL long long
char s1[MAXN+5],s2[MAXN+5];
int MOD;
LL A,B,b[MAXN+5],F;
int phi(int x){
int ps=x,res=x;
for(int i=2;i*i<=ps;i++)
if(x%i==0){
res=res/i*(i-1);
while(x%i==0)x/=i;
}
if(x!=1)res=res/x*(x-1);
return res;
}
int fst_pow(int a,int b,int p){
int res=1;
while(b){
if(b&1)res=(1LL*res*a)%p;
a=(1LL*a*a)%p;
b>>=1;
}return res;
}
int main()
{
scanf("%s%s%d",s1+1,s2+1,&MOD);
int l1=strlen(s1+1),l2=strlen(s2+1),P=phi(MOD);
for(int i=1;i<=l1;i++)A=(A*10+s1[i]-'0')%MOD;
for(int i=1;i<=l2;i++)b[i]=s2[i]-'0';
b[l2]--;
for(int i=l2;i>=1;i--)
if(b[i]<0)b[i-1]--,b[i]+=10;
else break;
for(int i=1;i<=l2;i++){
B=B*10+b[i];
if(B>=P)F=1,B%=P;
}
int Ans=1LL*(A-1+MOD)%MOD*fst_pow(A,F?B+P:B,MOD)%MOD;
printf("%d",Ans?Ans:MOD);
}
CF1033D Divisors
给你nnn个数a1 ana_1~a_na1 an,每个数都有3−53-53−5个因数,现在要求A=∏aiA=\prod a_iA=∏ai的因数个数,保证ai≤2×1018a_i≤2 \times 10^{18}ai≤2×1018。
其实我并不相信这题难度只有2000…
根据定理得∏(cnti+1)\prod (cnt_i+1)∏(cnti+1)
首先先分析满足条件的aaa组成只有:
p1p2p_1p_2p1p2,p12p_1^2p12,p13p_1^3p13,p14p_1^4p14
对于后面三个情况我们直接用C++ pow函数开方即可(注意精度)。
但是对于第一个情况就比较麻烦了…
有一种比较巧妙的做法,考虑答案只与相同的质因子有关,于是我们找其他与其他数的gcd,如果不是本身和1,就能得到p1,p2p_1,p_2p1,p2。如果没有公共因子,直接将答案乘上(cnt+1)2(cnt+1)^2(cnt+1)2即可。
复杂度:O(n2logmaxai)O(n^2 \log \max{a_i})O(n2logmaxai)。
这题的交互是用来搞笑的吗…
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define MAXN 500
#define eps 10e-6
#define MOD 998244353
#define LL long long
int n,vis[MAXN+5];
LL a[MAXN+5],ans;
vector<LL> g;
map<LL,int> D;
set<LL> s;
int main()
{
scanf("%d",&n);ans=1;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
LL p2=pow((double)a[i],0.5)+eps,p3=pow((double)a[i],1/3.0)+eps,p4=pow((double)a[i],0.25)+eps;
if(p4*p4*p4*p4==a[i]){D[p4]+=4;s.insert(p4);continue;}
if(p3*p3*p3==a[i]){D[p3]+=3;s.insert(p3);continue;}
if(p2*p2==a[i]){D[p2]+=2;s.insert(p2);continue;}
g.push_back(a[i]);
}
sort(g.begin(),g.end());
for(int i=0;i<g.size();i++){
int cnt=0;LL res=-1;
if(vis[i])continue;
for(int j=0;j<g.size();j++){
if(g[i]==g[j]){vis[j]=1;cnt++;continue;}
LL G=__gcd(g[i],g[j]);
if(G!=1&&G!=g[i])res=G;
}
for(auto x:s)
if(g[i]%x==0)res=x;
if(res==-1){ans=(ans*(cnt+1)*(cnt+1))%MOD;}
else D[res]+=cnt,D[g[i]/res]+=cnt;
}
for(auto x:D)
ans=(ans*(x.second+1))%MOD;
printf("%lld",ans);
}
CF582A GCD Table
给定一个N×MN\times MN×M的数表,其中第iii行第jjj个数为gcd(i,j)\gcd(i,j)gcd(i,j),再给给定一个长度K≤104K\leq 10^4K≤104的数列aaa,判断是否在数表的某一行出现过。 N,M≤1012N,M\leq 10^{12}N,M≤1012
简单题,我们每次挑当前最大的数,然后将之前选中的每个数取gcd\gcdgcd的数都删去两个即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
#define MAXN 250000
int n,tp;
int a[MAXN+5],cnt[MAXN+5],ans[MAXN+1];
map<int,int> id;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n*n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n*n;i++)
id[a[i]]++;
sort(a+1,a+n*n+1);
int pn=unique(a+1,a+n*n+1)-a-1;
for(int i=1;i<=pn;i++)
cnt[i]=id[a[i]],id[a[i]]=i;
for(int i=pn;i>=1;i--)
while(cnt[i]){
ans[++tp]=a[i];
printf("%d ",ans[tp]);
cnt[i]--;
for(int j=1;j<tp;j++)
cnt[id[__gcd(ans[tp],ans[j])]]-=2;
}
}
CF1027G X-mouse in the Campus
Part 2 专题归纳
数论基础定理
中国剩余定理/扩展中国剩余定理(CRT/EXCRT)
本文深入探讨了数论算法在编程竞赛中的应用,包括质因数分解、扩展欧拉定理、快速幂计算等核心技巧,并通过具体题目解析,如CF1034A、CF906D、CF17D等,展示了如何高效解决复杂数学问题。
877

被折叠的 条评论
为什么被折叠?



