数论专题


数论专题

传送门

https://codeforces.ml/problemset?order=BY_RATING_ASC&tags=math%2Cnumber+theory%2C1800-3000


D. Dima and Lisa

哥德巴赫猜想习题

判断奇数$n$是否能分解为至多$3$个素数之和。

1个素数的情况:本身为质数,$\sqrt{n}$特判。

2个素数的情况:奇=奇+偶,显然只需特判$2,n-2$是否为质数即可。

3个素数的情况:根据弱哥德巴赫猜想:大于$7$的奇数可分解为$3$个质数之和。

我们就先找到小于$n$的最大质数,然后减去它,这样$n$就很小且$n$为偶数。

根据强哥德巴赫猜想:大于$2$的偶数可分解两个质数之和。然后暴力找就可以了。

bool ck(int n){
    int mx=sqrt(n);
    for(int i=2;i<=mx;i++)
        if(n%i==0) return false;
    return true;
}
int main(){
    int n;
    scanf("%d",&n);
    if(ck(n)) return printf("1\n%d\n",n),0;
    if(ck(n-2)) return printf("2\n2 %d\n",n-2),0;
    for(int i=n-2;i>=2;i-=2){
        if(ck(i)){
            int x=n-i;
            for(int j=x-2;j>1;j--)
                if(ck(j)&&ck(x-j)){
                    printf("3\n%d %d %d\n",i,j,x-j);return 0;
                }
        }
    }
    return 0;
}

D. Same GCDs

$gcd$和欧拉函数定义。

$gcd(a,m)=g=gcd(gk_1,gk_2)=g$

$gcd(k_1,k_2)=1$

$gcd(a+x,m)=gcd((a+x)\pmod{m},m)=g$

$x’=(a+x)\bmod m \in[0,m-1]$

$gcd(a+x,m)=gcd(x’,m)=g$

$x’=x’’g,m=m’g$

$gcd(x’’,m’)=1$

$0\le x’<m\Rightarrow 0\le x’’<m’$

即:$ans=\varphi(m’)$

$\sqrt{n}$求$\varphi(m’)$即可。

ll phi(ll m){
    ll s=m;
    for(ll i=2;i*i<=m;i++)
        if(m%i==0){
            s-=s/i;
            while(m%i==0) m/=i;
        }
    if(m>1) s-=s/m;
    return s;
}
    scanf("%d",&t);while(t--){
        scanf("%lld%lld",&a,&m);
        ll g=__gcd(a,m);m/=g;
        printf("%lld\n",phi(m));
    }

C. Neko does Maths

$lcm(a+k,b+k)$ 最小,且$k$尽可能小。

令$g=gcd(a+k,b+k)$

令$ans=lcm(a+k,b+k)=\dfrac{(a+k)(b+k)}{g}$

$g=gcd(b-a,a+k)$

枚举$g|(b-a)$,只用在$(b-a)$的因子枚举,然后找到满足$g|(a+k)$的最小$k$,每次比较$lcm$,更新答案$k$。

复杂度:$O(\sqrt{b-a})$

ll a,b;
ll ans=1e18,k,d;
void f(ll x){
    ll y=(a+x-1)/x*x;
    ll lcm=y/x*(y+d);
    if(lcm<=ans) ans=lcm,k=y-a;
}
int main(){
    scanf("%lld%lld",&a,&b);
    if(a>b) swap(a,b);  d=b-a;
    for(ll i=1;i*i<=d;i++)
        if(d%i==0) f(i),f(d/i);
    printf("%lld\n",k);
    return 0;
}

1225D. Power Products

$\large ab=x^k,k\ge 2$

$\large a=x^{p_1},b=x^{p_2},p_1+p_2=k,(p_1,p_2\ge 0)$

对其进行质因数分解,并对指数$\pmod{k}$,则只需寻找质因数指数之和为$k$的数即可,这样我们可以用一个数组储存当前出现过的数,每次需要的数个数就是答案。

$a=p_1^{k_1}p_2^{k_2}\dots p_m^{k_m},b=p_1^{g_1}p_2^{g_2}\dots p_m^{g_m}$

则$k_i+g_i=k$

int n,k;
int a[N];
ll now,need;
ll ans;
void f(int x,int c){
    c%=k;
    int u=c,v=(k-c)%k;
    while(now&&u--){
        now*=x;
        if(now>N) now=0;
    }
    while(need&&v--){
        need*=x;
        if(need>N) need=0;
    }
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        now=need=1;
        int x;scanf("%d",&x);
        int mx=sqrt(x);
        for(int i=2;i<=mx;i++)
            if(x%i==0){
                int c=0;
                while(x%i==0) c++,x/=i;
                f(i,c);
            }
        if(x>1) f(x,1);
        ans+=a[need];
        a[now]++;
    }
    printf("%lld\n",ans);
    return 0;
}

时间复杂度:$O(n\sqrt{n})$

A. Nezzar and Board

裴蜀定理

https://harris.blog.csdn.net/article/details/113389644

B. Remainders Game

$x_1\equiv x_2\bmod c_i$

则$x_1-x_2\equiv 0\bmod {c_i}$

则$lcm(c_1,c_2\dots,c_n) | (x_1-x_2)$

而$(x_1-x_2)\pmod {k}\ne 0$

所以$lcm(c_1,c_2\dots,c_n)\pmod{k}\ne 0$

同时也是充要条件,构造$x=lcm(c_2,c_2\dots,c_n),y=2lcm(c_1,c_2\dots,c_n)$

为了防止$lcm$爆$long \ long$,可以一边$lcm$一边和$k$取$gcd$。最后看答案是否为$k$即可。

或者线筛一波,找到每个数的最大质因数,预处理出$lcm$的质因数分解。然后判断减掉$k$的所有质因数后是否还有多余的质因数。

ll gcd(ll a,ll b){
    return !b?a:gcd(b,a%b);
}
ll lcm(ll a,ll b){
    return a/gcd(a,b)*b;
}
int main(){
    int n,k;scanf("%d%d",&n,&k);
    ll s=1;
    for(int i=1;i<=n;i++){
        ll x;scanf("%lld",&x);
        s=gcd(k,lcm(s,x));
    }
    puts(s==k?"Yes":"No");
    return 0;
}

894B. Ralph And His Magic Field

当$n,m$奇偶性不同且$k=-1$无解,因为所有数的乘积按照行和列分别算是不同的。

否则,考虑填完前$n-1$行和前$m-1$列,一共有:$2^{(m-1)(n-1)}$种方案。

最后一行和最后一列是确定的。

ll ksm(ll a,ll n,ll m=mod){
    ll s=1;
    while(n){
        if(n&1) s=s*a%m;
        a=a*a%m;
        n>>=1;
    }
    return s;
}
ll n,m,k;
int main(){
    scanf("%lld%lld%lld",&n,&m,&k);
    if(((n+m)&1)&&k==-1) puts("0");
    else printf("%lld\n",ksm(ksm(2,n-1),m-1));
    return 0;
}

B1. Send Boxes to Alice (Easy Version)

枚举$k$ ,必然是$cnt_1$的因数较小的部分,然后分段模拟,每取移到一段的终点是最优的,最后取最小值即可。

code

vector<int>v;
ll ans=2e18;
void f(int x){
    ll s=0;
    int mx=SZ(v);
    for(int i=0;i<mx;i+=x){
        int p=v[(i+i+x-1)/2];
        for(int j=i;j<i+x;j++)
            s+=abs(p-v[j]);
    }
    ans=min(ans,s);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);
        if(x) v.pb(i);
    }
    sz=SZ(v);
    if(sz==1) return puts("-1"),0;
    for(int i=2;i*i<=sz;i++)
        if(sz%i==0){
            f(i);
            while(sz%i==0) sz/=i;
        }
    if(sz>1) f(sz);
    printf("%lld\n",ans);
     return 0;
}

C. Line

$ax+by+c=0$,求$x,y$整数解。这不扩欧$sb$题吗。一开始居然没看出来

void exgcd(ll a,ll b,ll &x,ll &y){
    if(!b) {x=1,y=0;return;}
    exgcd(b,a%b,y,x);
    y-=a/b*x;
}
int main(){
    scanf("%lld%lld%lld",&a,&b,&c);
    c=-c;
    ll g=__gcd(a,b);
    if(c%g) return puts("-1"),0;
    ll t=c/g;
    ll x,y;
    exgcd(a,b,x,y);
    x*=t,y*=t;
    ll b1=b/g*t,a1=a/g*t;
    while(x<-5e18&&b1>0){ //这一步还可以优化
        x+=b1;
        y-=a1;
    }
    if(x<-5e18||y>5e18) return puts("-1"),0;
    printf("%lld %lld\n",x,y);
    return 0;
}

963A. Alternating Sum

给了前$k$项,发现递推关系是$\large A_i=A_{i-k}(\dfrac{b}{a})^k$

$\large A_i=s_ia^{n-i}b^i,A_{i-k}=s_{i-k}a^{n-i+k}b^{i-k}$

$\large A_i=A_{i-k}(\dfrac{b}{a})^k$

显然可预处理$[A_0,A_{k-1}]$和他们的和。

$s=\sum\limits_{0}^{k-1} A_i$

$\sum\limits_{i=k}^{2k-1}=s\times (\dfrac{b}{a})^k$

是一个等比数列,令$q=(\dfrac{b}{a})^k$。

$cnt=\dfrac{n+1}{k}$

$sum=\dfrac{s(1-q^{cnt})}{1-q}$

余项暴力求。$q=1$ 需特判,因为此时分母为$0$了。

int main(){
    scanf("%lld%lld%lld%lld",&n,&a,&b,&k);
    ll inv=ksm(a,mod-2);
    ll q=ksm(b*inv%mod,k);
    scanf("%s",s);
    ll sum=0;
    for(int i=0;i<k;i++){
        if(s[i]=='+'){
            sum=(sum+ksm(a,n-i)*ksm(b,i)%mod)%mod;
        }
        else {
            sum=(sum-ksm(a,n-i)*ksm(b,i)%mod)%mod;
        }
    }
    sum=sum<mod?sum+mod:sum;
    ll cnt=(n+1)/k;
    ll tot;
    if(q==1){
        tot=sum*cnt%mod;
    }
    else tot=sum*(1-ksm(q,cnt))%mod*ksm(1-q,mod-2)%mod;
    ll rest=(n+1)%k;
    for(int i=n-rest+1;i<=n;i++){
        int j=i%k;
        if(s[j]=='+'){
            tot=(tot+ksm(a,n-i)*ksm(b,i)%mod)%mod;
        }
        else tot=(tot-ksm(a,n-i)*ksm(b,i)%mod)%mod;
    }
    printf("%lld\n",tot);
    return 0;
}

359C. Prime Number

$p$为质数,答案为$p^k$

对每个分子$v_i=sum-a_i$排序,从大到小,然后每次从后往前把相同的小的取出来,看个数是否$\pmod {x}=0$,如果不满足答案就是$p^{w}$。

否则合并一下,丢$\dfrac{cnt}{x}$个 $w+1$进去。

    for(int i=1;i<=n;i++)
        v.pb(sum-a[i]);
    sort(v.rbegin(),v.rend());
    while(1){
        ll w=v.back();
        ll cnt=0;
        while(!v.empty()&&v.back()==w){
            cnt++;v.pop_back();
        }
        if(cnt%x){
            w=min(w,sum);
            printf("%lld\n",ksm(x,w));
            return 0;
        }
        else {
            cnt/=x;
            for(int i=0;i<cnt;i++)
                v.pb(w+1);
        }
    }

文章作者: Harris-H
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Harris-H !
评论
  目录