zoukankan      html  css  js  c++  java
  • 数论基础

    T1

    第一种做法,考虑(a_0,a_1,b_0,b_1)的因子间的关系。
    对于任意一个因子,用(k)来表示该因子的数量。
    一定有

    • (k_{a0}>k_{a1})时,(k_x==k_{a1}),不然gcd一定不为(a_1)
    • (k_{a0}==k_{a1})时,只需要(k_x>=k_{a1})
    • (k_{a0}<k_{a1})时,gcd一定取不到(k_{a1}),此时无解

    对于(b)的分析也是同理

    • (k_{b0}<k_{b1})时,(k_x==k_{b1}),不然lcm一定不为(b_1)
    • (k_{b0}==k_{b1})时,只需要(k_x<=k_{b1})
    • (k_{b0}>k_{b1})时,lcm一定取不到(k_{b1}),此时无解

    最后用分步乘法原理把它乘起来就行
    这个解法个人感觉比较难写,但是我把它调出来了,我好棒??

    #include<cstdio>
    const int N=44800;
    bool nprime[N];
    int prime[N];
    void getp(int n){
    	for(int i=2;i<=n;i++){
    		if(!nprime[i])
    			prime[++prime[0]]=i;
    		for(int j=1;j<=prime[0]&&i*prime[j]<=n;j++){
    			nprime[i*prime[j]]=1;
    			if(i%prime[j]==0)
    				break;
    		}
    	}
    }
    int a0,a1,b0,b1;
    long long res;
    int find(int &x,int &t){
    	int cnt=0;
    	while(x%t==0){
    		cnt++;
    		x/=t;
    	}
    	return cnt;
    }
    bool calc(int t){
    	if(t>b1)return 1;
    	if(b1%t!=0)return 0;
    	int cnt=1;
    	int ka1=find(a1,t);
    	int ka0=find(a0,t);
    	int kb0=find(b0,t);
    	int kb1=find(b1,t);
    	if(ka1<ka0&&kb0<kb1&&ka1==kb1)cnt=1;
    	else if(ka1<ka0&&kb0==kb1&&ka1<=kb1)cnt=1;
    	else if(ka1==ka0&&kb0<kb1&&ka1<=kb1)cnt=1;
    	else if(ka1==ka0&&kb0==kb1&&ka1<=kb1)cnt=kb1+1-ka1;
    	else cnt=0;
    	res*=cnt;
    	return 0;
    }
    int main(){
    	getp(N-2);
    	int T;
    	scanf("%d",&T);
    	while(T--){
    		res=1;
    		scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
    		for(int i=1;i<=prime[0];i++)
    			if(calc(prime[i]))break;
    		if(b1!=1)calc(b1);
    		printf("%lld
    ",res);
    	}
    }
    

    第二种做法,考虑推导一下公式。
    已知(gcd(x,a_0)==a_1)(lcm(x,b_0)==b_1)
    由gcd的式子,可以推出(gcd(frac{x}{a_1},frac{a_0}{a_1})==1)
    注意lcm没有这个性质,但可以转化成gcd,即(xb_0over gcd(x,b_0))(==b_1)
    进一步化简得到,(xb_0==gcd(xb_1,b_0b_1))
    然后得到(gcd(frac{b_1}{b_0},frac{b_1}{x}))
    根据这两个式子,我们只需要枚举到(sqrt{b_1}),然后判断是不是符合条件就行。
    借一下(DarthVictor)大佬的代码,这个写法我没写

    
    
    #include<bits/stdc++.h>
    using namespace std;
    int gcd(int a,int b) {
        return b==0?a:gcd(b,a%b);
    }
    int main() {
        int T;
        cin>>T;
        while(T--) {
            int a0,a1,b0,b1;
            cin>>a0>>a1>>b0>>b1;
            int p=a0/a1,q=b1/b0,ans=0;
            for(int x=1;x*x<=b1;x++) 
                if(b1%x==0){
                    if(x%a1==0&&gcd(x/a1,p)==1&&gcd(q,b1/x)==1) ans++;
                    int y=b1/x;
                    if(x==y) continue; 
                    if(y%a1==0&&gcd(y/a1,p)==1&&gcd(q,b1/y)==1) ans++;
                }
            cout<<ans<<endl;
        }
        return 0;
    }
    

    T2

    这是一个裸的欧拉筛。。。。。

    #include<cstdio>
    const int N=3e6+10;
    int prime[N],phi[N];
    bool nprime[N];
    void init(int n){
    	phi[1]=1;
    	for(int i=2;i<=n;i++){
    		if(!nprime[i]){
    			prime[++prime[0]]=i;
    			phi[i]=i-1;
    		}
    		for(int j=1;j<=prime[0]&&i*prime[j]<=n;j++){
    			nprime[i*prime[j]]=1;
    			if(i%prime[j])
    				phi[i*prime[j]]=(prime[j]-1)*phi[i];
    			else {
    				phi[i*prime[j]]=prime[j]*phi[i];
    				break;
    			}
    		}
    	}
    }
    int main(){
    	init(N-5);
    	int a,b;
    	while(~scanf("%d%d",&a,&b)){
    		long long res=0;
    		for(int i=a;i<=b;i++)
    			res+=phi[i];
    		printf("%lld
    ",res);
    	}
    }
    

    T3

    不妨设(gcd(x,N)==k),那么(gcd(frac{x}{k},frac{N}{k})==1)
    所以枚举到(sqrt{N})找到因子,统计答案即(phi(frac{N}{k}))
    注意特判(i×i==N)的情况。

    #include<cstdio>
    int clac(int x){
        int res=1;
        for(int i=2;i*i<=x;i++){
            if(x%i)continue;
            x/=i;res*=i-1;
            while(x%i==0){
                x/=i;res*=i;
            }
        }
        if(x>1)res*=x-1;
        return res;
    }
    int main(){
        int T;
        scanf("%d",&T);
        while(T--){
            int res=0;
            int N,M;
            scanf("%d%d",&N,&M);
            for(int i=1;i*i<=N;i++){
                if(N%i)continue;
                if(i>=M)res+=clac(N/i);
                if(i*i!=N&&N/i>=M)res+=clac(i);
            }
            printf("%d
    ",res);
        }
    }
    

    T4

    举几个例子发现,如果(gcd(x,y)!=1),那么它一定会被前边的某个点遮住,所以要统计的即(gcd(x,y)==1)的情况。
    答案即,(sum_{x=0}^{n-1}sum_{y=0}^{n-1}gcd(x,y)==1)
    考虑化简这个式子,我们可以把中间的那一斜列拿掉,即(x==y)的情况,这种只对答案贡献1,然后就分为了两个三角形,不难发现这两个三角形对应的答案一样,因为对于每个三角形中的((a,b)),另一个三角形里边一定有一个((b,a))
    所以只考虑下边的三角形即可,发现答案化简为(2×sum_{x=1}^{n-1}sum_{y=0}^{x-1}gcd(x,y)==1)
    (2×sum_{i=1}^{n-1}phi(i)+1)
    注意对于(y=0)的情况,因为这时我们求(gcd(x,y))会直接返回(x)的值,而只有当(x==1)的时候(gcd)才为1,所以可以不用特判。

    #include<cstdio>
    const int N=4e4+10;
    bool nprime[N];
    int prime[N],phi[N];
    void init(int n){
    	phi[1]=1;
    	for(int i=2;i<=n;i++){
    		if(!nprime[i]){
    			prime[++prime[0]]=i;
    			phi[i]=i-1;
    		}
    		for(int j=1;j<=prime[0]&&i*prime[j]<=n;j++){
    			nprime[i*prime[j]]=1;
    			if(i%prime[j])
    				phi[i*prime[j]]=phi[i]*(prime[j]-1);
    			else {
    				phi[i*prime[j]]=phi[i]*prime[j];
    				break;
    			}
    		}
    		phi[i]+=phi[i-1];
    	}
    }
    int main(){
    	int n;
    	scanf("%d",&n);
    	init(n-1);
    	printf("%d
    ",2*phi[n-1]+1);
    }
    

    T5

    要求出(sum_{i=1}^Ngcd(i,N)),直接求出肯定会T掉。
    考虑变形为,(sum{d|N}d×sum_{i=1}^N(gcd(i,N)==d)),理解一下这个式子,最大公约数一定是(N)的约数,所以每有一个(gcd==d)就会对答案产生一个(d)的贡献。
    然后(sum_{i=1}^N(gcd(i,N)==d)==sum_{i=1}^N(gcd(frac{i}{d},frac{N}{d})==1)==phi(frac{N}{d}))
    仍旧是只枚举到(sqrt{N})

    #include<cstdio>
    #define ll long long 
    ll euler(ll x){
        ll res=1;
        for(int i=2;i*i<=x;i++){
            if(x%i)continue;
            x/=i;res*=i-1;
            while(x%i==0){
                x/=i;res*=i;
            }
        }
        if(x>1)res*=x-1;
        return res;
    }
    int main(){
        ll n,res=0;
        scanf("%lld",&n);
        for(int i=1;i*i<=n;i++){
            if(n%i)continue;
            res+=i*euler(n/i);
            if(i*i!=n)res+=n/i*euler(i);
        }
        printf("%lld
    ",res);
    }
    

    T6

    因为阶乘乘出来十分大,所以需要边乘边取mod,我不认为有人愿意写高精度
    因为(M!|N!),与(M!)互质的数一共有(frac{N!}{M!}×phi(M!))个,乘开化简后可以得到(N!×prod_i(frac{p_i-1}{p_i}))
    所以只需要在跑欧拉筛的时候顺便维护一下逆元和阶乘即可。
    看起来是对的,但是被PinkRabbit大佬hack了。。。。
    正解目前也看的不是很懂,先咕了。

    #include<cstdio>
    #define F(i,a,b) for(int i=(a);i<=(b);++i)
    #define F2(i,a,b) for(int i=(a);i<(b);++i)
    int T,Mod,n,m;
    int primes[664580], pnum=0;
    bool isn_prime[10000001];
    int pi[664580],inv[10000001];
    int in[664580],fct[10000001];
    int pos[10000001];
    void init(){
        isn_prime[0]=isn_prime[1]=1;
        F(i,2,10000000){
            if(!isn_prime[i]) primes[++pnum]=i;
            for(int j=1;j<=pnum&&primes[j]*i<=10000000;++j){
                isn_prime[primes[j]*i]=1;
                if(i%primes[j]==0) break;
            }
        }
        inv[1]=1; for(int i=2;i<Mod&&i<=10000000;++i)
            inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;
        pi[0]=1; F(i,1,pnum) pi[i]=1ll*pi[i-1]*(primes[i]-1)%Mod;
        in[0]=1; F(i,1,pnum) if(primes[i]!=Mod) in[i]=1ll*in[i-1]*inv[primes[i]%Mod]%Mod; else in[i]=in[i-1];
        fct[0]=1; F(i,1,10000000) if(i!=Mod) fct[i]=1ll*fct[i-1]*i%Mod; else fct[i]=fct[i-1];
        F(i,2,10000000) if(isn_prime[i]) pos[i]=pos[i-1]; else pos[i]=pos[i-1]+1; 
    }
    int main(){
        scanf("%d%d",&T,&Mod);
        init();
        while(T--){
            scanf("%d%d",&n,&m);
            if(n>=Mod&&m<Mod) puts("0");
            else printf("%d
    ",1ll*fct[n]*pi[pos[m]]%Mod*in[pos[m]]%Mod);
        }
        return 0;
    }
    

    T7
    exgcd裸题,要求(axequiv1pmod{b})
    可以把它变化成为(ax-by=1)
    因为只求(x),所以变换(y)的系数没关系,得到(ax+by==1)
    因为一定有解,所以,(gcd(a,b)==1),所以(bx'+a%by'==1)
    然后得到(ax+by==bx'+(a-lfloorfrac{a}{b} floor)y')
    又因为求整数解,所以让对应的系数相等即可。
    得到(x=(a-lfloorfrac{a}{b} floor)y'),(y=x')

    #include<cstdio>
    #define ll long long
    void exgcd(ll a,ll b,ll &x,ll &y){
    	if(b==0){
    		x=1;
    		y=0;
    		return ;
    	}
    	exgcd(b,a%b,y,x);
    	y-=a/b*x;
    }
    int main(){
    	ll a,b,x,y;
    	scanf("%lld%lld",&a,&b);
    	exgcd(a,b,x,y);
    	printf("%lld
    ",(x%b+b)%b);
    }
    

    T8

    还是个exgcd。
    假设跳了(k)次后相遇,那么(x+kmequiv y+nkpmod{L})
    化简可以得到(k(n-m)+pL==x-y),其中p为常数。
    然后只要解出(k)即可。
    但这里让求最小整数解,所以考虑转化。
    令特解为(x_0,y_0),那么有
    (ax_0+by_0==ax+by)
    (a(x_0-x)==b(y-y_0))
    (frac{a}{gcd(a,b)}(x_0-x)==frac{b}{gcd(a,b)}(y-y_0))
    这时由于系数是互质的,
    (x_0-x==frac{b}{gcd(a,b)}),
    所以(x==x_0-frac{b}{gcd(a,b)})

    #include<cstdio>
    #define ll long long
    ll exgcd(ll a,ll b,ll &x,ll &y){
    	if(b==0){
    		x=1;
    		y=0;
    		return a;
    	}
    	ll g=exgcd(b,a%b,y,x);
    	y-=a/b*x;
    	return g;
    }
    int main(){
    	ll x,y,n,m,L;
    	scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&L);
    	ll a=n-m,c=x-y,b=L;
    	if(a<0)a=-a,c=-c;
    	ll g=exgcd(a,b,x,y);
    	if(m==n||c%g)
    		printf("Impossible
    ");
    	else
    		printf("%lld
    ",(c/g*x%(b/g)+b/g)%(b/g));
    }
    

    T9

    思路基本和上一个差不多,主要考虑什么时候得到体积最小
    (V==-ax+by),如果它要是有解,那么(gcd(a,b)|V),所以(V_min==gcd(a,b))

    #include<cstdio>
    #define lqs long long
    lqs exgcd(lqs a,lqs b,lqs &x,lqs &y){
    	if(b==0){
    		x=1;
    		y=0;
    		return a;
    	}
    	lqs g=exgcd(b,a%b,y,x);
    	y-=a/b*x;
    	return g;
    }
    int main(){
    	lqs a,b,x,y;
    	scanf("%lld%lld",&a,&b);
    	lqs g=exgcd(a,b,x,y);
    	printf("%lld
    ",g);
    	x*=-1;a*=-1;
    	while(x<0||y<0){
    		x+=(x<0)?b/g:0;
    		y-=(x>=0)?a/g:0;
    	}
    	printf("%lld %lld
    ",x,y);
    }
    

    T10

    显然是递推求逆元。
    (Mod=ki+r),则有(ki+requiv 0pmod{Mod})
    同时乘上(i^{-1}r^{-1})得到(kr^{-1}+i_{-1}equiv 0pmod{Mod})
    所以(i^{-1}equiv-kr^{-1}pmod{Mod})
    (i^{-1}equiv-lfloorfrac{Mod}{i} floor(p%i)^{-1})

    T11

    一种是去找循环节,但是时间复杂度显然很SPFA。
    易得当(x<=frac{n}{2})时,(x=2x)
    (x>frac{n}{2})时,(x=2x-n-1)
    那么有没有什么好的办法来用一个统一的式子表示呢?
    显然(2xequiv 2x-n-1 pmod{n+1})
    所以只要%上一个(n+1)即可,最后得到的答案也不会受到影响因为起始位置一定小于(n+1)
    所以解方程(2^Mx==Lpmod{n+1})即可。
    解得方法有很多,随便挑一种就行。
    还有洛谷上的数据经过魔改所以过不掉

    #include<iostream>
    using namespace std;
    typedef long long ll;
    ll pow(ll a,ll b,ll c){
    	ll ans=1;
    	while(b){
    		if(b&1)ans=ans*a%c;
    		a=a*a%c;
    		b>>=1;
    	}
    	return ans;
    }
    void exgcd(ll a,ll b,ll &x,ll &y){
    	if(b==0){
    		x=1;
    		y=0;
    		return;
    	}
    	exgcd(b,a%b,y,x);
    	y-=a/b*x;
    }
    int main(){
    	ll n,m,l,x,y;
    	cin>>n>>m>>l;
    	exgcd(pow(2,m,n+1),n+1,x,y);
    	cout<<(l*x%(n+1)+n+1)%(n+1);	
    }
    

    尾声

    赶来赶去最后终于是在十一点之前搞完了,正如题目说的那样,数论基础,数学的学习才刚刚开始,后边还有更大的挑战。
    由于写的匆忙,所以有些地方可能会写错,只是笔误,欢迎指正。
    代码可以复制粘贴但有些题目代码加有防伪标记,咳咳咳咳

  • 相关阅读:
    交换实验
    路由引入和控制
    ISIS
    BGP联盟
    BGP2
    bgp
    Linux日常总结
    配置本地yum源方法
    达梦数据库常见问题-安装
    达梦数据库常见问题-安装
  • 原文地址:https://www.cnblogs.com/anyixing-fly/p/12980162.html
Copyright © 2011-2022 走看看