数论题集

链接:https://ac.nowcoder.com/acm/contest/903/B
来源:牛客网

解析:

一开始想用逆元,但发现q-1不一定和mod互质,所以不能用直接逆元

ac:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,q,p;

ll mult(ll a,ll b,ll p) {
    ll r=0;
    ll t=a;
    while(b) {
        if(b&1) r=(r+t)%p;
        t=(t<<1)%p;
        b>>=1;
    }
    return r;
}

ll qpow(ll a,ll b,ll mod)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=mult(ans,a,mod)%mod;
        a=(mult(a,a,mod))%mod;
        b>>=1;
    }
    return ans%mod;
}

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        cin>>q>>n>>p;
        ll k=qpow(q,n+1,p*(q-1))-q;
        k=k/(q-1);
        printf("%lld\n",k);
    }
    return 0;
}

因为包含取模,无法直接逆元所以这里用公式递归求

求等比为k的等比数列之和S[n]:

当n为偶数..S[n] = S[n/2] + pow(k,n/2) * S[n/2]

当n为奇数...S[n] = S[n/2] + pow(k,n/2) * S[n/2] + pow(k,n)等比数列第n个数的值

直接递归求解

ac:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

ll n,q,p;

ll qpow(ll a,ll b,ll mod)
{
    ll res=1;
    while(b)
    {
        if(b&1)
            res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res;
}

ll solve(ll q,ll n,ll p)
{
    if(n==1)
        return q%p;
    if(n%2==0)
        return (1+qpow(q,n/2,p))*solve(q,n/2,p)%p;
    else
        return ((1+qpow(q,n/2,p))*solve(q,n/2,p)%p+qpow(q,n,p))%p;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld%lld%lld",&q,&n,&p);
        printf("%lld\n",solve(q,n,p));
    }
    return 0;
}

 

余数之和:https://vjudge.net/problem/HYSBZ-1257

解析:

取模的意义:

 

\sum_{i=1}^{n}k\%i=>\sum_{i=1}^{n}(k-(k/i)*i)=>\sum_{i=1}^{n}k-\sum_{i=1}^{n}i*(k/i)=n*k-\sum_{i=1}^{n}i*(k/i)

后面可以用整除分块解决,复杂度就降为O( k)

整除分块\sum_{i=1}^{n}\lfloor\frac{n}{i}\rfloor整除分块代码:

for(int l=1,r;l<=n;l=r+1)
{
    r=n/(n/l);
    ans+=(r-l+1)*(n/l);
}

代码:

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;

int main()
{
    ll k,n,ans,sum=0;
    cin>>n>>k;
    ans=k*n;//整除分块代码+等差数列求和
    if(k<=n)
    {
        for(int l=1,r;l<=k;l=r+1)
        {
            r=k/(k/l);
            sum+=(r-l+1)*(k/l)*(l+r)>>1;
        }
    }
    else{
        for(int l=1,r;l<=n;l=r+1)//k>n,r<=n
        {
            r=min(k/(k/l),n);
            sum+=(r-l+1)*(k/l)*(l+r)>>1;
        }
    }
    printf("%lld\n",ans-sum);
    return 0;
}

https://vjudge.net/contest/313367#problem/E

题意:

给1个数n,输出4个数,这四个数和为n,且都是素数

如果无法输出,则输出:Impossible.

解析:

哥德巴赫猜想的一个扩展,首先筛素数

根据猜想得:

如果n<8,肯定无法输出

如果为偶数,分解为:2 2 x n-4-x

如果为奇数,分解为:2 3 x n-5-x

暴力枚举

ac:

#include<bits/stdc++.h>
#define ll long long
#define MAXN 10000005
using namespace std;
int prime[MAXN];//保存素数
bool vis[MAXN];//初始化
int cnt;

void getprime(int n)
{
	cnt=0;
	memset(vis,0,sizeof(vis));
	for(int i=2;i<n;i++)
	{
		if(!vis[i])
            prime[cnt++]=i;
		for(int j=0;j<cnt&&i*prime[j]<n;j++)
		{
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)
                break;
		}
	}
}

int main()
{
    ll n;
    getprime(10000001);
    while(scanf("%lld",&n)!=EOF)
    {
        if(n<8)
        {
            printf("Impossible.\n");
        }
        else{
            if(n%2==1)
            {
                for(int i=2;i<n-5;i++)
                {
                    if(vis[i]==0&&vis[n-i-5]==0)
                    {
                        printf("2 3 %d %d\n",i,n-i-5);
                        break;
                    }
                }
            }
            else{
               for(int i=2;i<n-4;i++)
                {
                    if(vis[i]==0&&vis[n-i-4]==0)
                    {
                        printf("2 2 %d %d\n",i,n-i-4);
                        break;
                    }
                }
            }
        }
    }
    return 0;
}

https://vjudge.net/problem/HDU-2421

题意:

给定一个数n=A^B,求该数的因子的因子数的立方和

例如:

n=2^2,因子有1,2,4,因子数1,2,3,ans=(1)^3+(2)^3+(3)^3=36

解析:

唯一分解定理

积性函数:对于任意互质的整数a和b有性质f(ab)=f(a)*f(b)的数论函数

学习链接:https://www.cnblogs.com/zhoushuyu/p/8275530.html

立方和公式:1^3 + 2^3 + …… n^3 = (1+2+3+...+n)^2 = (n (n+1) / 2)^2

补一个平方和公式:1^2+2^2+...+n^3=n*(n+1)*(2*n+1)/6

这里将A唯一分解得:A=a1^p1*a2^p2*....an^pn,因子数为:(1+p1)*(1+p2)*(1+p3)*...(1+pn)

这里有次方=>因子数为:(1+p1*b)*(1+p2*b)*(1+p3*b)*....*(1+pn*b).

ans=(1+p1*b)^3+(1+p2^b)^3+....+(1+pn^b)^3

ans=[(1+p1*b)*(1+p1*b+1)/2]^2+....[(1+pn*b)*(1+pn*b+1)/2]^2

ac:

#include<bits/stdc++.h>
#define mod 10007
#define ll long long
#define MAXN 1005
using namespace std;
int pri[MAXN];
int sumx[MAXN];
int index=0;

//pri是素因子,sumx是该素因子上的指数b,第一个素因子坐标为0
void getpri(ll m)//分解质因数
{
    index=0;
    memset(sumx,0,sizeof(sumx));
	for(ll i=2;i*i<=m;i++)
	{
		while(m%i==0)
		{
			pri[index]=i;
			sumx[index]++;
			m/=i;
		}
		if(sumx[index]) index++;
    }
    if(m>1) pri[index]=m, sumx[index++]=1;
}

int main()
{
    ll a,b,cas=1;
    while(scanf("%lld%lld",&a,&b)!=EOF)
    {
        getpri(a);
        ll sum=1;
        for(int i=0;i<index;i++)
        {
            ll n=(sumx[i]*b+1)%mod;//(1+pi*b)
            ll s=((n)*(n+1)/2)%mod;//s=n*(n+1)/2
            sum=(sum*((s*s)%mod))%mod;//sum+=s*s;
        }
        printf("Case %lld: %lld\n",cas++,sum);
    }
    return 0;
}

https://vjudge.net/problem/HDU-4542

题意:

1.求最小的因子数为k的数

2.求最小的(n-因子数)==k的数

解析:

对于2,答案可能没有,如果有,呢么不会很大

对于1,答案肯定有,如果是一个很大的素数,呢么就肯定会超过2^62

例如:17359,这个凑不出因子,只能是2的指数,肯定大于2^62

我们暴力1e19以内的所以情况

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#define ll long long
using namespace std;
unsigned ll n;
unsigned ll prime[17]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,51};
unsigned ll cc[220005];

void dfs(int pos,int len,unsigned ll q,int num)//第pos个素数,前一位素因子指数len,num当前因子个数,q为该数的值
{
    if(q<cc[num])//对于同样数目的因子,我们取最小的数,存起来
        cc[num]=q;
    ll g=1;
    for(int i=1;i<=len;i++)
    {
        g=g*prime[pos];
        if(n/g<q)//不能用q*g<n,会越界
            break;
        dfs(pos+1,i,q*g,num*(i+1));
    }
}

int vis[100005]={0};
int b[500005];

void init()
{
    for(int i=1;i<=100000;i++)
    {
        b[i]=1e9;
        for(int j=1;j*i<=100000;j++)
            vis[j*i]++;
    }
    for(int i=1;i<=100000;i++)
    {
        vis[i]=i-vis[i];
        b[vis[i]]=min(b[vis[i]],i);
    }
}

int main()
{
    int t,ty,k,cas=1;
    for(int i=0;i<=50000;i++)
        cc[i]=1e19;
    n=1e19;
    dfs(1,62,1,1);//这里最小的2的指数最多62
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&ty,&k);
        printf("Case %d: ",cas++);
        if(ty==0)
        {
            if(cc[k]==1e19)
                printf("INF\n");
            else
                printf("%lld\n",cc[k]);
        }
        else{
            if(b[k]==1e9)
                printf("Illegal\n");
            else
                printf("%d\n",b[k]);
        }
    }
    return 0;
}

https://vjudge.net/problem/HDU-5584

题意:

1只青蛙只能向下或者向右跳,如果在(x,y)坐标,它可以跳到(x+lcm(x,y),y)或者(x,y+lcm(x,y));

给你青蛙的终点,它能从多少种坐标过来

解析:

1.如果x>y,lcm(x,y)>=x,y,所以一定是坐标大的被改变了,回跳以后,路径还是只有一个,所以路径唯一

2.设gcd(x,y)=k,x=nk,y=mk,lcm(x,y)=nmk,那么下一步能走到(n(m+1)k,mk)或者(nk,m(n+1)k),并且由于n(m+1)与m互质,n与m(n+1)互质,所以下一步的gcd依然是k.

3.根据以上两点,就可以用逆推找出答案,每次都假设x>y,x=nk,y=mk,当前点就是由(x/(y/k+1),y)走到的,如果x不再是(y+k)的倍数(即:(y/k+1)*k的倍数),则表示不能再逆推

ac:

#include<cstdio>
#include<algorithm>
using namespace std;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int main(){
    int x,y,T;
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++){
        scanf("%d%d",&x,&y);
        if(x<y) swap(x,y);
        int k=gcd(x,y),cnt=1;
        while(x%(y+k)==0){
            cnt++;
            x=x/(y/k+1);
            if(x<y) swap(x,y);
        }
        printf("Case #%d: %d\n",cas,cnt);
    }
    return 0;
}

我自己用的笨办法,假设ex较大,我发现

ex=x+lcm(x,ey)=>ex=x+x*ey/gcd(x,ey)=x(1+ey)/gcd(x,ey),直接枚举x,x是ex的因子,耗时较大

ac:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll gcd(ll a,ll b) {while(b^=a^=b^=a%=b);return a;}

int main()
{
    ll t,ex,ey,cas=1;
    cin>>t;
    while(t--)
    {
        int cnt=0;
        cin>>ex>>ey;
        if(ex<ey)
            swap(ex,ey);
        ll ga=-1,gb=-1;
        while(1)
        {
            for(int i=1;i*i<=ex;i++)
            {
                if(ex%i==0)
                {
                    int x=i;
                    if(x*(1+ey/gcd(ey,x))==ex){
                        cnt++;
                        ex=x;
                        break;
                    }
                    x=ex/i;
                    if(x*(1+ey/(gcd(ey,x)))==ex){
                        cnt++;
                        ex=x;
                        break;
                    }
                }
            }
            if(ex<ey)
                swap(ex,ey);
            if(ga==ex&&gb==ey)
                break;
            ga=ex,gb=ey;
        }
        printf("Case #%lld: %d\n",cas++,cnt+1);
    }
    return 0;
}
/*
3
6 10
6 8
2 8
*/

https://vjudge.net/problem/POJ-3101

题意:

n个行星开始在一条直线上且在同一边,问你最少再过多久,他们会再一次全面在同一直线上

解析:

ui=2*π/Ti,选择行星0为参考点,行星i的相对角速度为

t1=(T0*T1)/(2*(abs(T1-T0))

import java.math.*;
import java.util.*;

public class Main {

    public static void main(String[ ]  args)
    {
        Scanner cin = new Scanner(System.in);
        int n,cnt;
        int [ ] t  =new  int[1100];
        int [ ]s = new int [1100];
        BigInteger a,b,g,mo = null,de = null;
        while(cin.hasNext())
        {
            n = cin.nextInt();
            for(int i = 0;i<n;i++)
                s[i] = cin.nextInt();

            for(int i = 1;i<n;i++)
            {
                a = BigInteger.valueOf(s[i]*s[0]);//t0*t1
                b = BigInteger.valueOf(Math.abs(s[i]-s[0])*2);//2*(t0-t1)
                g =  a.gcd(b);
                if(i == 1)
                {
                    mo = a.divide(g);
                    de = b.divide(g);
                }
                else
                {
                    a  = a.divide(g);
                    b = b.divide(g);
                    mo = mo.multiply(a).divide(mo.gcd(a));
                    de = de.gcd(b);
                }
            }

            System.out.println(mo+" "+de);
        }
        cin.close();
    }
}

https://vjudge.net/problem/CodeForces-1114C

求n!下末尾0的个数

解析:

x在b进制下,可以写成a*b^k

等价于n!%(b^k)==0的最大k

要求 n! 在 b 进制下有多少个尾 0 就相当于 求 n! % (b^k) == 0 的最大 k。

那么我们现在把 n! 看作一个数 A。问题就是 求 A % (b^k) == 0 的最大 k;

我们知道有素数分解定理: b = p1^a1 * p2^a2 * p3^a3 ...;

那么我们如果可以求得 A 里面 p1^b1 * p2^b2 * p3^b3 ...  的 b1, b2, b3...

那么答案 遍历所有b中质因子^ai,求最小指数ans = min(ans,bi/ai ) 

ac:

#include<bits/stdc++.h>
#define ll long long
#define N 10010
using namespace std;
 
const ll MAXN=1e18+1;
 
ll pri[N];//质因子
ll sumx[N];//该质因子的指数最大值
ll index=0;//可以分解出多少个质因子
 
 
void getpri(ll m)//质因数分解
{
	for(ll i=2;i*i<=m;i++)
	{
		while(m%i==0)
		{
			pri[index]=i;
			sumx[index]++;
			m/=i;
		}
		if(sumx[index]) index++;
    }
    if(m>1) pri[index]=m, sumx[index++]=1;
}
 
ll getsumx(ll n,ll p)//n!能分解出sumX个p
{                    //返回n!中p因子的指数
	ll sumX=0;
	while(n>0)
	{
		sumX+=n/p;
		n/=p;
	}
	return sumX;
}
 
int main()
{
    ll n,b,ans=MAXN;
    scanf("%I64d%I64d",&n,&b);
    getpri(b);
    for(ll i=0;i<index;i++)
    {
        ans=min(ans,getsumx(n,pri[i])/sumx[i]);
    }
    printf("%I64d",ans);
    return 0;
}

https://vjudge.net/problem/Gym-101981J

题意:

给定n个数

mul(l,r) = \prod_{i=l}^{r}a_i,fac(l,r)=mul(l,r),求\sum_{i=1}^{n}\sum_{j=i}^{n}fac(i,j)

解析:

对于一个点ai,求\sum_{i=1}^{n}\sum_{j=i}^{n}{ai}

n-i+1是后面的区间端点总数,i是前面的区间端点总数

但是在这里,一个区间内素因子可能重复,i的效果肯定小于(n-i+1)*(i)

对于在i位置一个素因子,我们规定向后取满,对于最靠近i的前面重复的在j位置我们同一个素因子

i位置的素因子在前面取到的最大端点为(i-j),这个素因子的贡献为(n-i+1)*(i-j)

线筛a[i]的素因子,保存每个素因子出现的位置

对所以素因子求贡献

#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#define MAXN 1000005
#define ll long long
using namespace std;
 
int tot;
int isprime[MAXN];
int prime[MAXN];
int a[MAXN];
vector<int> pos[MAXN];//保存素数
 
void get_prime() //欧拉筛
{
    tot=0;
    memset(isprime,1,sizeof(isprime));
    isprime[0]=isprime[1]=0;
    for(int i=2;i<=MAXN;i++)
    {
        if(isprime[i]) prime[tot++]=i;
        for(int j=0;j<tot;j++)
        {
            if(i*prime[j]>MAXN) break;
            isprime[i*prime[j]]=0;
            if(i%prime[j]==0) break;
        }
    }
}
 
void dec(int p)//分解素因子
{
    int n=a[p];
    for(int i=0;i<tot&&prime[i]*prime[i]<=n;i++)
    {
        if(n%prime[i]==0)
        {
            pos[prime[i]].push_back(p);//保存素因子存在的位置
            while(n%prime[i]==0)
                n/=prime[i];
        }
    }
    if(n>1) pos[n].push_back(p);
}
 
int main()
{
    int n;
    get_prime();
    scanf("%d",&n);
    for(int i=0;i<tot;i++)
        pos[prime[i]].push_back(0);
 
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        dec(i);
    }
    ll sum=0;
 
    for(int i=0;i<tot;i++)
    {
        for(int j=1;j<pos[prime[i]].size();j++)
        {
            sum+=(ll)(n+1-pos[prime[i]][j])*(pos[prime[i]][j]-pos[prime[i]][j-1]);
        }
    }
    printf("%lld\n",sum);
    return 0;
}

https://vjudge.net/problem/HDU-4002

解析:

求 2到n之间  n/phi(n) 为最大值时的n.

phi(n)=n*(1-1/p1)*(1-1/p2)*......(1-1/pk)

即求:(1-1/p1)*(1-1/p2)*......(1-1/pk)

自然是素因子越多越好

由此
2=2;

6=2*3;

30=2*3*5;

120=2*3*5*7;

2310=2*3*5*7*11;

ac:

#include<stdio.h>
#include<iostream>
#include<string.h>
#define ll long long
#define MAXN 1005
#define L 100005
using namespace std;
 
bool isprime[MAXN];
int prime[MAXN];
int tot=0;
int na[L];
string xc[1001];
 
void getprime(int N)//素数筛
{
    memset(isprime,true,sizeof(isprime));
    memset(prime,0,sizeof(prime));
    isprime[0]=false;
    isprime[1]=false;
    for(int i=2;i<=N;i++)
    {
        if(isprime[i])
            prime[++tot]=i;
        for(int j=1;j<=tot&&i*prime[j]<N;j++)
        {
            isprime[i*prime[j]]=false;
            if(!(i%prime[j]))
                break;
        }
    }
}
 
string mul(string a,ll b)//高精度a乘单精度b
{
    string ans;
    ll La=a.size();
    fill(na,na+L,0);
    for(int i=La-1;i>=0;i--) na[La-i-1]=a[i]-'0';
    ll w=0;
    for(int i=0;i<La;i++) na[i]=na[i]*b+w,w=na[i]/10,na[i]=na[i]%10;
    while(w) na[La++]=w%10,w/=10;
    La--;
    while(La>=0) ans+=na[La--]+'0';
    return ans;
}
 
int cmp(string a,string b)//简单正整数比较
{
    int lena=a.size();
    int lenb=b.size();
    if(lena>lenb)
        return 1;
    else if(lena<lenb)
        return -1;
    else{
        for(int i=0;i<lena;i++)
        {
            if(a[i]-'0'>b[i]-'0')
                return 1;
            else if(a[i]-'0'<b[i]-'0')
                return -1;
        }
        return 0;
    }
}
 
int bsearch(int n, string key)//二分查找
{
    int low = 0;
    int high = n;
    int mid = 0;
    while(low <= high) {
        mid = low + ((high-low) >> 1);
        if(cmp(key,xc[mid])==0||cmp(key,xc[mid])==-1){
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return low <= n ? low : -1;
}
 
int main()
{
    std::ios::sync_with_stdio(false);
    int x,y,sum,t;
    string cc="1",ct;
    getprime(MAXN);
    for(int i=1;i<=55;i++)
    {
        cc=mul(cc,prime[i]);
        xc[i]=cc;
        if(cc.size()>100)
            break;
    }
    cin>>t;
    while(t--)
    {
        cin>>ct;
        int q=bsearch(54,ct);
        if(xc[q].compare(ct)==0)
            cout<<xc[q]<<endl;
        else cout<<xc[q-1]<<endl;
    }
    return 0;
}

B. Goldbach

题意:

t组测试数据

给一个n(偶数),2<=n<2^63,将n分解为两个素数

输出两个素数,结果符合就可以

ac:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
typedef unsigned long long ll;
using namespace std;

ll add_mod(ll a,ll b,ll mod){
    ll ans=0;
    while(b){
        if(b&1)
            ans=(ans+a)%mod;
        a=a*2%mod;
        b>>=1;
    }
    return ans;
}

ll pow_mod(ll a,ll n,ll mod){
    if(n>1){
        ll tmp=pow_mod(a,n>>1,mod)%mod;
        tmp=add_mod(tmp,tmp,mod);
        if(n&1) tmp=add_mod(tmp,a,mod);
        return tmp;
    }
    return a;
}

bool Miller_Rabbin(ll n,ll a){
    ll d=n-1,s=0,i;
    while(!(d&1)){
        d>>=1;
        s++;
    }
    ll t=pow_mod(a,d,n);
    if(t==1 || t==-1)
        return 1;
    for(i=0;i<s;i++){
        if(t==n-1)
            return 1;
        t=add_mod(t,t,n);
    }
    return 0;
}

bool is_prime(ll n){
    ll i,tab[4]={3,4,7,11};
    for(i=0;i<4;i++){
        if(n==tab[i])
            return 1;
        if(!n%tab[i])
            return 0;
        if(n>tab[i] && !Miller_Rabbin(n,tab[i]))
            return 0;
    }
    return 1;
}

bool isprime[1000001];
int prime[1000001];
int tot=0;

void getprime(int N)
{
    memset(isprime,true,sizeof(isprime));
    memset(prime,0,sizeof(prime));
    isprime[0]=false;
    isprime[1]=false;
    for(int i=2;i<=N;i++)
    {
        if(isprime[i])
            prime[++tot]=i;
        for(int j=1;j<=tot&&i*prime[j]<N;j++)
        {
            isprime[i*prime[j]]=false;
            if(!(i%prime[j]))
                break;
        }
    }
}

int main()
{
    int t;
    getprime(1000000);
    cin>>t;
    while(t--)
    {
        ll n,m;
        cin>>n;
        for(int i=1;i<tot;i++)
        {
            m=n-prime[i];
            if(is_prime(m))
            {
                cout<<prime[i]<<" "<<m<<endl;
                break;
            }
        }
    }
    return 0;
}

https://codeforces.com/problemset/problem/906/D

题意:

给你1~n的wi,让你求这种式子,q次询问,每次询问wl^wl+1^wl+2^....^wr

解析:

广义欧拉降幂

a^b\equiv \begin{cases} a^{b\%\phi(p)}~~~~~~~~~~~gcd(a,p)=1\\ a^b~~~~~~~~~~~~~~~~~~gcd(a,p)\neq1,b<\phi(p)\\ a^{b\%\phi(p)+\phi(p)}~~~~gcd(a,p)\neq1,b\geq\phi(p) \end{cases}~~~~~~~(mod~p)

如果b^c>phi(p):a^{b^c}\ \ mod \ \ p\equiv a^{b^c\%\varphi(p)+\varphi(p)} \ \ mod \ \ p,否则次方就不加phi(p)

我们可以递归求解.

求a^b^c,先求b^c%phi(p),一直递归下去,如果phi(p)==1,就没有必要递归下去了.

我们改一下快速幂函数,让他对于大于mod的值,返回res%mod+mod.

这里询问非常多,我们还要将phi记忆化,避免重复求phi,否则会超时

ac:

#include<bits/stdc++.h>
#define MAXN 100005
#define ll long long
using namespace std;
ll w[MAXN];
unordered_map<ll,ll> p;

ll MOD(ll a,ll b)
{
    if(a>b)
        return a=a%b+b;
    else
        return a;
}

ll phi(ll n)
{
    if(p[n]!=0)
        return p[n];
    ll res = n,k=n;
    for (ll i = 2; i*i <= n; i++)
        if (n%i == 0)
        {
            res =res/i*(i-1);
            while(n%i == 0)
                n /= i;
        }
    if (n > 1)
        res = res/n*(n-1);
    p[k]=res;//算出的答案存起来,避免重复计算
    return res;
}

ll qpow(ll a,ll b,ll mod)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a,ans=MOD(ans,mod);
        a=a*a,a=MOD(a,mod);
        b>>=1;
    }
    return ans;
}

ll solve(ll l,ll r,ll mod)
{
    if(l==r||mod==1)
        return MOD(w[l],mod);
    return qpow(w[l],solve(l+1,r,phi(mod)),mod);
}

int main()
{
    ll n,k,l,r;
    ll m;
    while(scanf("%lld%lld",&n,&m)!=EOF)
    {
        for(ll i=1;i<=n;i++)
            scanf("%lld",&w[i]);
        scanf("%lld",&k);
        for(ll i=1;i<=k;i++)
        {
            scanf("%lld%lld",&l,&r);
            printf("%lld\n",solve(l,r,m)%m);
        }
    }
    return 0;
}

http://codeforces.com/gym/102028/problem/E

题意:

RR=1/(1/R1+1/R2+1/R3+....+1/Rn)

Ri=i,如果i的因子有一个数的二次,Ri就为无穷大

Si={j,i%j==0},Si包含i的所以因子

解析:

为了让RR竟可能的小,呢就就让1/R1+1/R2+....+1/R2,尽可能的大,竟可能的多

=>Ri越小越好,然后Ri的集合有是一个数的因子,呢么如果Ri与Rj相等,呢么Ri*Rj为d^2,无穷大,倒一下->0,白白多加一个因子

再根据测试数据,很容易退出10->1,2,3     选择连续素数的积<=n

1/(1/1+1/2+1/3)=1/2

这里n非常大,用java过

ac:

import javafx.scene.transform.Scale;
import org.omg.Messaging.SYNC_WITH_TRANSPORT;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        int prime[]=new int[1005],k=1;
        prime[1]=1;
        for(int a=2;a<=500;a++){
            int sign=0;
            for(int b=2;b*b<=a;b++){
                if(a%b==0){
                    sign=1;
                    break;
                }
            }
            if(sign==0){
                prime[++k]=a;
            }
        }
        Scanner in=new Scanner(System.in);
        int t;
        t=in.nextInt();
        while(t!=0)
        {
            BigInteger AA,BB,c,EE,DD;
            BB= BigInteger.valueOf(1);
            AA=in.nextBigInteger();
            EE=BigInteger.valueOf(1);
            DD=BigInteger.valueOf(1);
            for(int i=2;i<=k;i++)
            {
                c=BigInteger.valueOf(prime[i]);
                BB = BB.multiply(c);
                if(BB.compareTo(AA)==1){
                    break;
                }
                EE=EE.multiply(c.add(BigInteger.valueOf(1)));
                DD=BB;
            }
            BigInteger gcc=EE.gcd(DD);
            System.out.println(DD.divide(gcc)+"/"+EE.divide(gcc));
            t--;
        }
    }
}

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值