【已整理】【数论】【总】【水】

1.lightoj 1341

题意:给你一个a表示矩形(不能是正方形)的面积,给你一个b表示最小的可能的边长,问你这样的矩形有多少对。我觉得这个要是多来几个极端数据,包括我的程序以及网上的题解都会T。可能这题专门就是为了考察因子个数的计算方法吧。因子个数只要素因数分解一下就求出来。然后题目给的应该都是a比较大,b很小的样例,这样如果暴力枚举b到根号a的数,就会T。所以只能先算因子个数,再暴力枚举b。但是如果只是这样写,也可以专门出数据卡时间(比如b也很大),所以说这题可能专门就是为了考察素因子分解吧。我自己写的程序是根据b的范围讨论了一下,但是也没有太大的卵用,还是可以出数据卡时间。

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

const int MAXN=1000005;
int prime[MAXN/10];
bool vis[MAXN];
int tot;

void get_prime(){
    tot=0;
    memset(vis,false,sizeof(vis));
    for(int i=2;i<MAXN;i++){
        if(!vis[i]){
            prime[tot++]=i;
            for(int j=i+i;j<MAXN;j+=i)
                vis[j]=true;
        }
    }
}

ll decap(ll num){
    ll res=1;
    for(int i=0;i<tot&&prime[i]*prime[i]<=num;i++){
        if(num%prime[i])    continue;
        ll tt=0;
        while(num%prime[i]==0){
            tt++;
            num/=prime[i];
        }
        res*=(tt+1);
    }
    if(num>1)
        res*=2;
    return res;
}

int main(){
    get_prime();
    int t;
    ll a,b,c;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++){
        scanf("%lld%lld",&a,&b);
        ll tt=sqrt(a);
        if(b>tt){
            printf("Case %d: 0\n",cas);
            continue;
        }
        ll res=0;
        if(b<=10000){
            for(ll i=1;i<b&&i<tt;i++){
                if(a%i==0)
                    res++;
            }
            res=decap(a)/2-res;
        }
        else{
            for(ll i=b;i<tt;i++){
                if(a%i==0)
                    res++;
            }
            if(tt*tt<a&&a%tt==0)
                res++;
        }
        printf("Case %d: %lld\n",cas,res);
    }
    return 0;

}
View Code

根据这题得出了一个结论,一个数的因子,两两相乘的等于原数的因子对数等于因子个数/2,这里的除法是向下取整除法。首先,一个数的因子,大于根号n,小于根号n的因子个数一定是一一对应,所以个数是相等的,但是根号n也可能是一个因子,所以一个数的因子数如果是奇数个,则根号n也一定是其中一个因子,所以因子对数是向下取整除以2。

 2.lightoj 1213

题意:给了你一段代码。代码的意思是,从n个数里,可重复的选择k个数,res=(res+求和(a【所有选择的k个数】))%mod

在每个位置,每个数都可以被选择,则一共有n^k种,每次选出了k个数,则一共选出了k*n^k,由于每个数被选的概率相等,每个数被选出k*n^(k-1)次。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long int ll;
const int MAXN=36666;
ll mod;

ll quick(ll n,ll k){
    n%=mod;
    ll res=1;
    while(k){
        if(k&1)
            res=(res*n)%mod;
        n=(n*n)%mod;
        k>>=1;
    }
    return res;
}

int main(){
    int t;
    ll n,k,a;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++){
        scanf("%lld%lld%lld",&n,&k,&mod);
        ll tt=quick(n,k-1);
        tt=(tt*k)%mod;
        ll res=0;
        for(int i=0;i<n;i++){
            scanf("%lld",&a);
            res=(res+a*tt)%mod;
        }
        printf("Case %d: %lld\n",cas,res);
    }
    return 0;
}
View Code

 3.poj2116 Death to Binary?

这题虽然是水题,但是还是有坑点,就是0要单独表示成一个0。还学到了一个东西,用斐波那契数列表示一个数。

F[0]=1,F[1]=2,F[n]=F[n-1]+F[n-2]。

第i位的权值为F[i]

这题告诉我们一个结论,所有的数都可以用斐波那契进制表示出来。且某些数的表示方法不止一种,但是没有连续相邻两个1的表示方法是唯一的。

我观察了一下,发现一个规律,给你一个十进制数n,让你表示成一个斐波那契进制的数,则一定是找到最接近n的斐波那契数,这一位为1,这一位同时也是最高位,n-=这个斐波那契数。递归下去,直到n变为0,整个斐波那契数就构造出来了,且这个斐波那契数满足没有相邻的1的条件。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long int ll;
const int MAXN=87;
char a[MAXN],b[MAXN],c[MAXN];
ll f[MAXN];

void init(){
    f[0]=1;f[1]=2;
    for(int i=2;i<MAXN;i++){
        f[i]=f[i-1]+f[i-2];
    }
}

int solve(char *a,ll num){
    for(int i=0;i<MAXN;i++)
        a[i]='0';
    int cnt=-1;
    while(num){
        int loc=upper_bound(f,f+MAXN,num)-f-1;
        num-=f[loc];
        if(cnt==-1)
            cnt=loc;
        a[loc]='1';
    }
    return cnt;
}

void print(char *s,int len){
    for(int i=len-1;i>=0;i--)
        printf("%c",s[i]);
    printf("\n");
}

void fun(){
    int res=0;
    for(int i=0;i<40;i++)
        res+=f[i];
    printf("%d\n",res);
}

int main(){
    int t;
    init();
    while(~scanf("%s%s",a,b)){
        int lena=strlen(a);
        int lenb=strlen(b);
        int lenc;
        ll aa=0,bb=0,cc;
        for(int i=0;i<lena;i++){
            if(a[i]=='1')
                aa+=f[lena-i-1];
        }
        for(int i=0;i<lenb;i++){
            if(b[i]=='1')
                bb+=f[lenb-i-1];
        }
        cc=aa+bb;
        lena=solve(a,aa)+1;
        lenb=solve(b,bb)+1;
        lenc=solve(c,cc)+1;
        if(lena==0) lena++;
        if(lenb==0) lenb++;
        if(lenc==0) lenc++;
        for(int i=0;i<lenc+2-lena;i++)
            printf(" ");
        if(aa>0)
            print(a,lena);
        else
            printf("0\n");
        printf("+");
        for(int i=1;i<lenc+2-lenb;i++)
            printf(" ");
        if(bb>0)
            print(b,lenb);
        else
            printf("0\n");
        printf("  ");
        for(int i=0;i<lenc;i++)
            printf("-");
        printf("\n");
        printf("  ");
        if(cc>0)
            print(c,lenc);
        else
            printf("0\n");
        printf("\n");
    }
    return 0;
}
View Code

 3.poj2429 GCD & LCM Inverse 

题意:给你最大公因数和最小公倍数,让你求原来的两个数,如果有多解,输出两个数的和最小的情况。

首先两个数肯定都至少有最大公因数那么大,然后就是最小公倍数/最大公因数的这个数的分配问题

举个例子,30和10800,素因数分解后分别为2*3*5和2*2*2*2*3*3*3*5*5。那么两者至少都有2*3*5。然后需要分配2*2*2*3*3*5。我们不能将同一个素因数拆开来分给两者,所以分配的实际上是8*9*5。那么原题就转化为了给定矩形的面积,求最小周长,爆搜一下即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
#include<set>
#include<cmath>

using namespace std;
typedef long long int ll;
const int MAXN=1000005;
ll factor[MAXN];
int tot;
const int S=5;
ll muti_mod(ll a,ll b,ll c){    //返回(a*b) mod c,a,b,c<2^63
    a%=c;
    b%=c;
    ll ret=0;
    while (b){
        if (b&1){
            ret+=a;
            if (ret>=c) ret-=c;
        }
        a<<=1;
        if (a>=c) a-=c;
        b>>=1;
    }
    return ret;
}

ll pow_mod(ll x,ll n,ll mod){  //返回x^n mod c ,非递归版
    if (n==1) return x%mod;
    int bit[90],k=0;
    while (n){
        bit[k++]=n&1;
        n>>=1;
    }
    ll ret=1;
    for (k=k-1;k>=0;k--){
        ret=muti_mod(ret,ret,mod);
        if (bit[k]==1) ret=muti_mod(ret,x,mod);
    }
    return ret;
}

bool check(ll a,ll n,ll x,ll t){   //以a为基,n-1=x*2^t,检验n是不是合数
    ll ret=pow_mod(a,x,n),last=ret;
    for (int i=1;i<=t;i++){
        ret=muti_mod(ret,ret,n);
        if (ret==1 && last!=1 && last!=n-1) return 1;
        last=ret;
    }
    if (ret!=1) return 1;
    return 0;
}

bool Miller_Rabin(ll n){
    if(n==1)
        return false;
    ll x=n-1,t=0;
    while ((x&1)==0) x>>=1,t++;
    bool flag=true;
    if (t>=1 && (x&1)==1){
        for (int k=0;k<S;k++){
            ll a=rand()%(n-1)+1;
            if (check(a,n,x,t)) {flag=true;break;}
            flag=0;
        }
    }
    if (!flag || n==2) return true;
    return false;
}

ll gcd(ll a,ll b){
    if (a==0) return 1;
    if (a<0) return gcd(-a,b);
    while (b){
        ll t=a%b; a=b; b=t;
    }
    return a;
}

ll Pollard_rho(ll x,ll c){
    ll i=1,x0=rand()%x,y=x0,k=2;
    while (1){
        i++;
        x0=(muti_mod(x0,x0,x)+c)%x;
        ll d=gcd(y-x0,x);
        if (d!=1 && d!=x){
            return d;
        }
        if (y==x0) return x;
        if (i==k){
            y=x0;
            k+=k;
        }
    }
}

void findfac(ll n){           //递归进行质因数分解N
    if (Miller_Rabin(n)){
        factor[tot++] = n;
        return;
    }
    ll p=n;
    while (p>=n) p=Pollard_rho(p,rand() % (n-1) +1);
    findfac(p);
    findfac(n/p);
}

ll res=1;
ll bound;
ll number;

void dfs(int u,ll num){
    if(u>=tot){
        if(bound==-1||number/num+num<bound){
            bound=number/num+num;
            res=num;
        }
        return;
    }
    dfs(u+1,num);
    dfs(u+1,num*factor[u]);
}

int main(){
    ll n,m;
    while(~scanf("%lld%lld",&n,&m)){
        if(n==m){
            printf("%lld %lld\n",n,m);
            continue;
        }
        ll tt=m/n;
        tot=0;
        findfac(tt);
        sort(factor,factor+tot);
        int j=0;
        ll pre=factor[0];
        for(int i=1;i<tot;i++){
            if(factor[i]!=pre){
                factor[++j]=factor[i];
                pre=factor[j];
            }
            else
                factor[j]*=pre;
        }
        tot=j+1;
        number=tt;
        bound=-1;
        dfs(0,1);
        ll low=n*res,high=n*(number/res);
        if(low>high)
            swap(low,high);
        printf("%lld %lld\n",low,high);
    }
    return 0;
}
View Code

 

posted @ 2017-08-21 16:12  nearlight  阅读(83)  评论(0)    收藏  举报