zoukankan      html  css  js  c++  java
  • 数论练习

    (T) 为测试数据组数,时限默认 (1s)

    A. (Tle 10000,nle 10^8) ,求 ( ext{lcm}(1,2,dots,n)\% 2^{32})(3s)

    首先 (O(n)) 线性筛,处理出质数的前缀积,时间吃得消,但空间吃不消,所以把线性筛中那个bool数组改成bitset
    然后 (O(log^2n)) 回答每个询问,枚举一个 (i) ,再二分出 (p^ile n)(p^{i+1}>n) 的所有质数 (p) ,把答案乘上它们的积,用unsigned保存。

    Code

    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int maxn=100000003;
    typedef unsigned int U;
    int pr[6000003],tot;
    U bit[(maxn>>5)+3],mul[6000003];
    void set(int i){bit[i>>5]|=(1u<<(i&31));}
    bool at(int i){return (bit[i>>5]>>(i&31))&1;}
    void init(int n){
    	set(1);
    	mul[0]=1;
    	for(int i=2;i<=n;i++){
    		if(!at(i)){
    			pr[++tot]=i;
    			mul[tot]=mul[tot-1]*i;
    		}
    		for(int j=1;j<=tot&&i*pr[j]<=n;j++){
    			set(i*pr[j]);
    			if(i%pr[j]==0)break;
    		}
    	}
    }
    int main(){
    	init(100000000);
    	int T;
    	scanf("%d",&T);
    	for(int TT=1;TT<=T;TT++){
    		int n;
    		scanf("%d",&n);
    		U ANS=1;
    		for(int i=1;i<=27;i++){
    			int l=1,r=tot,mid,ans=0;
    			while(l<=r){
    				mid=(l+r)>>1;
    				if(pow(pr[mid],i)<=n)ans=mid,l=mid+1;
    				else r=mid-1;
    			}
    			ANS*=mul[ans];
    		}
    		printf("Case %d: %u
    ",TT,ANS);
    	}
    	return 0;
    }
    
    

    B. (Tle 2*10^5,nle 3*10^6) ,求 (sum_{i=1}^n sum_{j=i+1}^n ext{lcm}(i,j)\% 2^{64})

    (f[n]=sum_{i=1}^{n-1} ext{lcm}(i,n))
    (f[n]=n*sum frac{i}{gcd(i,n)})
    (f[n]=sum frac{n}{d}*frac{i}{d}*d)
    (ecause frac{n}{d})(frac{i}{d}) 互质
    ( herefore f[n]=sum frac{2*φ(frac{n}{d})}{frac{n}{d}}*n)
    (f[n]=sum_{i=1,j|n}^{n-1} frac{2*φ(j)}{j}*n)

    Code

    #include<bits/stdc++.h>
    using namespace std;
    typedef unsigned long long D;
    const int maxn=3000003;
    D ANS[maxn];
    bool check[maxn];
    int pr[300003],tot,phi[maxn];
    void init(int n){
    	phi[1]=1;	
    	for(int i=2;i<=n;i++){
    		if(!check[i]){
    			pr[++tot]=i;
    			phi[i]=i-1;
    		}
    		for(int j=1;j<=tot&&i*pr[j]<=n;j++){
    			check[i*pr[j]]=1;
    			if(i%pr[j]==0){
    				phi[i*pr[j]]=phi[i]*pr[j];
    				break;
    			}
    			else phi[i*pr[j]]=phi[i]*phi[pr[j]];
    		}
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=i;j<=n;j+=i){
    			ANS[j]+=D(i)*phi[i]/2*j;
    		}
    		ANS[i]+=ANS[i-1];
    	}
    }
    int main(){
    	init(3000000);
    	int T;
    	scanf("%d",&T);
    	for(int TT=1;TT<=T;TT++){
    		int n;
    		scanf("%d",&n);
    		printf("Case %d: %llu
    ",TT,ANS[n]);
    	}
    	return 0;
    }
    

    C. (Tle 325,a,ble 10^6,Lle 10^{12}) ,求最小的 (c) ,使得 ( ext{lcm}(a,b,c)=L)

    首先求出 (x= ext{lcm}(a,b))
    然后请自行思考。简单题。

    D. (Tle 2000,kle nle 10^6) ,求 (C(n,k)\% 1000003)

    (C(n,k)=n!*k!^{-1}*(n-k)!^{-1})
    水题。(由于 (C(n,k)) 不可能有因子 (1000003) ,所以不用lucas)

    E. (T=10000,-10^8le all;numbersle 10^8) ,求方程 (Ax+By+C=0(x_1le xle x_2,y_1le yle y_2)) 的整数解个数。

    exgcd+特判。
    细节繁多,注意处理边界。

    Code

    #include<bits/stdc++.h>
    #define f(x) ((-c-a*(x))/b)
    using namespace std;
    typedef long long D;
    D gcd(D a,D b){return b==0?a:gcd(b,a%b);}
    D egcd(D a,D b,D& x,D& y){
    	if(b==0){
    		x=1;
    		y=0;
    		return a;
    	}
    	D ans=egcd(b,a%b,x,y);
    	D tmp=x;
    	x=y;
    	y=tmp-a/b*y;
    	return ans;
    }
    D cal(D a,D b,D c){
    	D x,y;
    	D g=egcd(a,b,x,y);
    	if(c%g!=0)return -1;
    	x*=c/g;
    	b=abs(b);
    	D ans=x%b;
    	if(ans<=0)ans+=b;
    	return ans;
    }
    signed main(){
    	int T;
    	scanf("%d",&T);
    	for(int TT=1;TT<=T;TT++){
    		D a,b,g,c,xl,xr,yl,yr,x,y,ans=0;
    		double f1,f2;
    		scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&xl,&xr,&yl,&yr);
    		if(a==0||b==0){
    			if(a==0){
    				if(b==0){
    					if(c)ans=0;
    					else ans=(D)(xr-xl+1)*(yr-yl+1);
    				}
    				else{
    					if(c%b==0&&-c/b>=yl&&-c/b<=yr)ans=xr-xl+1;
    					else ans=0;
    				}
    			}
    			else if(b==0){
    				if(c%a==0&&-c/a>=xl&&-c/a<=xr)ans=yr-yl+1;
    				else ans=0;
    			}
    		}
    		else{
    			x=cal(a,b,-c);
    			y=f(x);
    			g=gcd(a,b);
    			if(x!=-1){
    				D x_l,x_r,y_l,y_r;
    				if((D)g*b<0){
    					x_l=ceil(double(xr-x)*g/b);
    					x_r=floor(double(xl-x)*g/b);
    				}
    				else{
    					x_l=ceil(double(xl-x)*g/b);
    					x_r=floor(double(xr-x)*g/b);
    				}
    				if((D)g*a<0){
    					y_l=ceil(double(y-yl)*g/a);
    					y_r=floor(double(y-yr)*g/a);
    				}
    				else{
    					y_l=ceil(double(y-yr)*g/a);
    					y_r=floor(double(y-yl)*g/a);
    				}
    				D l=max(x_l,y_l),r=min(x_r,y_r);
    				if(l<=r)ans=r-l+1;
    			}
    		}
    		printf("Case %d: %lld
    ",TT,ans);
    	}
    	return 0;
    }
    

    F. (Tle 300,x,nle 10^6) ,求 (sum_{a=1}^n sum_{b=1}^n gcd(x^a-1,x^b-1))

    首先 (gcd(x^a-1,x^b-1)=x^{gcd(a,b)}-1)
    证明:见这篇博客
    经过推算可得, (gcd(i,n)=m)(i) 的个数 (=φ(n/m))
    对于每个 (m)(gcd(i,j)=m)((i,j)) 的个数是 ((sum_{k=1}^{n/m} φ(k))*2-1)
    因此我们可以除法分块,每一个块内 (n/m) 相等,那么每一个块 ([l,r]) 的答案就是 ((sum_{i=l}^r (x^i-1))*((sum_{k=1}^{n/m} φ(k))*2-1))

    Code

    #include<cstdio>
    using namespace std;
    const int maxn=1000003,mod=1000000007;
    int phi[maxn],pr[100003],tot,sum[maxn];
    bool check[maxn];
    int Plus(int x,int y){return (x+=y)>=mod?x-mod:x;}
    int Minus(int x,int y){return (x+=mod-y)>=mod?x-mod:x;}
    int mul(long long x,int y){return x*y%mod;}
    int qpow(int x,int y){
    	int ans=1;
    	while(y){
    		if(y&1)ans=mul(ans,x);
    		x=mul(x,x);
    		y>>=1;
    	}
    	return ans;
    }
    int Div(int x,int y){return mul(x,qpow(y,mod-2));}
    void init(int n){
    	phi[1]=1;
    	for(int i=2;i<=n;i++){
    		if(!check[i]){
    			pr[++tot]=i;
    			phi[i]=i-1;
    		}
    		for(int j=1;j<=tot&&i*pr[j]<=n;j++){
    			check[i*pr[j]]=1;
    			if(i%pr[j]==0){
    				phi[i*pr[j]]=phi[i]*pr[j];
    				break;
    			}
    			else phi[i*pr[j]]=phi[i]*phi[pr[j]];
    		}
    	}
    	for(int i=1;i<=n;i++)sum[i]=Plus(sum[i-1],phi[i]);
    }
    int get_sum_from_x_l_to_x_r(int x,int l,int r){
    	return x==1?r-l+1:Div(mul(qpow(x,l),Minus(qpow(x,r-l+1),1)),Minus(x,1));
    }
    int calc(int x,int n,int i,int j){
    	return mul(Minus(get_sum_from_x_l_to_x_r(x,i,j),j-i+1),Minus(mul(sum[n/i],2),1));
    }
    int main(){
    	init(1000000);
    	int T;
    	scanf("%d",&T);
    	while(T--){
    		int x,n,ans=0;
    		scanf("%d%d",&x,&n);
    		for(int i=1,j;i<=n;i=j+1){
    			j=n/(n/i);
    			ans=Plus(ans,calc(x,n,i,j));
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    其他题目

    LOJ 6392 密码学第三次小作业

    (ecause gcd(e_1,e_2)=1)
    ( herefore e_1x+e_2y=1)
    ( herefore m^{e_1x+e_2y}=m=c_1^xc_2^y)
    所以只需要快速幂。如果 (x<0||y<0) 就计算逆元。

    Codeforces 487C

    题意

    询问是否存在 (1)(n) 的排列 (P) ,使得对于每个 (1le ile n)((prod_{j=1}^i P_j) mod n) 互不相同。如果存在则构造一组方案。

    首先第一个数是 (1) ,第 (n) 个数是 (n)
    然后如果 (n>4)(n) 是合数就不可能。原因: (n>4)(n) 是合数时,((n-1)!≡0(\% n))
    如果 (n>4)(n) 是质数,那么构造 (a[i]=i/(i-1)mod n)
    证明:假设存在 (a_i≡a_j(\% n))
    那么 (i/(i-1)≡j/(j-1)(\% n))
    (1+frac{1}{i-1}≡1+frac{1}{j-1}(\% n))
    (i≡j(\% n))
    矛盾。所以这种构造方案是正确的。

    NOI2018 屠龙勇士

    选剑的话用multiset维护。
    然后列方程:设选的剑攻击力为 (t_i) ,那么 (a_i≡x_i*t_i(\% p_i)) ,用exgcd解出,作为一个方程放到方程组 (x≡x_i(\% p_i)) 里用excrt解出。
    然后由于每条龙血量必须攻击到 (le 0)

    Code

    #include<cstdio>
    #include<cmath>
    #include<set>
    using namespace std;
    typedef long long D;
    const int maxn=200003;
    D g;
    D egcd(D a,D b,D &x,D &y){
        if(b==0){
            x=1;
            y=0;
            return a;
        }
        D ans=egcd(b,a%b,x,y);
        D tmp=x;
        x=y;
        y=tmp-a/b*y;
        return ans;
    }
    D cal(D a,D b,D c){
        D x,y;
        g=egcd(a,b,x,y);
        if(c%g!=0)return -1;
        b=abs(b/g);
        x=(x*__int128(c/g)%b+b)%b;
        return x;
    }
    D calc(D a,D b,D m){ // ax===b(%m)
        return cal(a,m,b);
    }
    D excrt(int n,D a[],D m[]){
        D x=a[1],l=m[1];
        for(int i=2;i<=n;i++){
            D t=calc(l,(m[i]+a[i]-x%m[i])%m[i],m[i]);
            if(t==-1)return -1;
            x=(x+__int128(t)*l)%(l*(m[i]/g));
            l*=m[i]/g;
        }
        return x;
    }
    int n,m;
    D a[maxn],p[maxn],t[maxn],A[maxn],M[maxn],mx;
    multiset<D> st;
    multiset<D>::iterator it;
    int main(){
        int T;
        scanf("%d",&T);
        while(T--){
            st.clear();
            mx=0;
            bool flag=1;
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++)scanf("%lld",a+i);
            for(int i=1;i<=n;i++)scanf("%lld",p+i);
            for(int i=1;i<=n;i++)scanf("%lld",t+i);
            for(int i=1;i<=m;i++){
                D x;
                scanf("%lld",&x);
                st.insert(x);
            }
            for(int i=1;i<=n;i++){
                it=st.upper_bound(a[i]);
                if(it!=st.begin())it--;
                D s=*it;
                A[i]=cal(s,p[i],a[i]);
                if(A[i]==-1){flag=0;break;}
                M[i]=p[i]/g;
                mx=max(mx,(a[i]+s-1)/s);
                st.erase(it);
                st.insert(t[i]);
            }
            if(!flag)puts("-1");
            else{ 
                D ans=excrt(n,A,M);
                if(ans==-1)puts("-1");
                else{
                    ans+=max(0ll,(mx-ans+M[n]-1)/M[n]);
                    printf("%lld
    ",ans);
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    第一次作业
    第0次作业
    第14、15周作业
    第七周作业
    第六周作业
    第四周作业
    第三周作业
    第4次作业
    第3次作业
    第二次作业
  • 原文地址:https://www.cnblogs.com/BlogOfchc1234567890/p/10885605.html
Copyright © 2011-2022 走看看