zoukankan      html  css  js  c++  java
  • 数论某些题目

    1.平面上有一个圆, 圆心坐标为(0,0),半径为n. 问圆周上有多少个整点. 整点的定义即x,y坐标均为整数的点.

    做题过程:思考后感觉有点难度,可能用到一些奇怪的知识,于是决定去看题解;

    题解:

    1.一般人都能想到x2+y2=r2这一点,但是仅想到这一点并没什么用;

    2.变形:

       移项得:r2-x2=y2

       得y2=(r-x)(r+x)

       设d=gcd((r-x),(r+x))

       则设A=(r-x)/d,B=(r+x)/d

       代入原式得 y2=ABd2

       因为gcd(A,B)=1

       所以A,B均为完全平方数

       设a2=A,b2=B

       可得a2+b2=2r/d

    3.得到上面的式子,d很显然是2r的约数,可以枚举d;

    在枚举d时,可以枚举a,算出b,若gcd(a,b)等于1,则ans++即可;

    个人理解:实际上它这个最后的式子相比于最初的式子有什么优点呢?

    第一个是r的次数由2次方变成了一次方,于是就可以发现原来的一个O(n)级别的算法成了根号级别的算法;

    本质上是通过数学运算除去原本枚举时的无用状态;

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<string>
     5 #include<ctime>
     6 #include<algorithm>
     7 #include<cstdlib>
     8 #include<map>
     9 #include<set>
    10 #include<vector>
    11 #include<queue>
    12 using namespace std;
    13 #define FILE "dealing"
    14 #define LL long long 
    15 #define up(i,j,n) for(int i=j;i<=n;i++)
    16 #define pii pair<int,int>
    17 #define piii pair<int,pair<int,int> >
    18 template<typename T> inline bool chkmin(T &a,T b){return a>b?a=b,true:false;}
    19 namespace IO{
    20     char *fs,*ft,buf[1<<15];
    21     inline char gc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
    22     inline int read(){
    23         int x=0,ch=gc();bool f=0;
    24         while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();}
    25         while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();}
    26         return f?-x:x;
    27     }
    28 }using namespace IO;
    29 namespace OI{
    30     LL n;
    31     LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);}
    32     LL cnt=0;
    33     void slove(){
    34         scanf("%lld",&n);
    35         for(LL d=1;d*d<=2*n;d++){
    36             if(!(2*n%d)){
    37                 for(LL a=1;a*a<=(n/d);a++){
    38                     LL b=(LL)sqrt(2.0*n/d-a*a);
    39                     if(a*a+b*b==2*n/d&&gcd(a,b)==1&&a!=b)cnt++;
    40                 }
    41                 d=2*n/d;
    42                 for(LL a=1;a*a<=(n/d);a++){
    43                     LL b=(LL)sqrt(2.0*n/d-a*a);
    44                     if(a*a+b*b==2*n/d&&gcd(a,b)==1&&a!=b)cnt++;
    45                 }
    46                 d=2*n/d;
    47             }
    48         }
    49         cnt=4*cnt+4;
    50         printf("%lld
    ",cnt);
    51     }
    52 }
    53 int main(){
    54     freopen(FILE".in","r",stdin);
    55     freopen(FILE".out","w",stdout);
    56     using namespace OI;
    57     slove();
    58     return 0;
    59 }
    View Code

     2.一共有4种硬币,面值分别为c1,c2,c3,c4. 阿Q带着一些硬币去商店买东西,他带了d1枚第一种硬币,d2枚第二种硬币,d3枚第三种硬币,d4枚第四种硬币,若想买一个价值为s的东西,问阿Q有多少种付coins的方法.
    比如c={1,2,5,10},d={3,2,3,1},s=10,一共有4种方法:
    10=1+1+1+2+5
    10=1+2+2+5
    10=5+5
    10=10
    注意,阿Q可能会去很多次商店,每次带的硬币数量和要买的东西价值可能不一样,你需要对每一次都求出方法总数。

    以前做过的题目再做一遍;

    题解:

    1.先预处理出来f数组,f[i]表示这四种面值的钱凑成i面额有多少种方案;

    2.然后利用容斥原理,对每次询问,用全部限制的方案数-限制第一种物品的方案数-限制第二种物品的方案数-限制第三种物品的方案数-限制第四种物品的方案数+限制第一,二种物品的方案数......+限制1,2,3,4物品的方案数;

    累计完之后便是答案;

    为什么?

    以限制第一种物品为例,它需要凑成s钱数,如果使用(d[1]+1)份以上的1物品,就不被允许,因此就需要用f[s]减去使用四种硬币凑成s-(d[1]+1)*c[1]的方案数,同理减去2,3,4种硬币不符要求的方案数;

    但会发现多减了,因为有类似第一种第二种物品都超过了限制的情况被减去了两次,因此要加回来......容斥原理大致就是这么个意思;

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<algorithm>
    #include<cstdlib>
    #include<map>
    #include<set>
    #include<vector>
    #include<queue>
    #include<cmath>
    using namespace std;
    #define FILE "dealing"
    #define LL long long 
    #define up(i,j,n) for(int i=j;i<=n;i++)
    #define pii pair<int,int>
    #define piii pair<int,pair<int,int> >
    template<typename T> inline bool chkmin(T &a,T b){return a>b?a=b,true:false;}
    namespace IO{
        char *fs,*ft,buf[1<<15];
        inline char gc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
        inline int read(){
            int x=0,ch=gc();bool f=0;
            while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();}
            while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();}
            return f?-x:x;
        }
    }using namespace IO;
    namespace OI{
        const int maxn(101000);
        LL c[5],d[5],f[maxn],q,s;
        void slove(){
            up(i,1,4)c[i]=read();q=read();
            f[0]=1;
            up(k,1,4)up(i,c[k],100000)f[i]+=f[i-c[k]];
            while(q--){
                up(i,1,4)d[i]=read();
                s=read();
                LL ans=0;
                for(int i=0;i<(1<<4);i++){
                    LL cnt=0,sum=0;
                    up(j,1,4)if(i&(1<<(j-1)))cnt++,sum+=(d[j]+1)*c[j];
                    sum=s-sum<0?0:f[s-sum];
                    if(cnt%2==0)ans+=sum;
                    else ans-=sum;
                }
                printf("%lld
    ",ans);
            }
            
        }
    }
    int main(){
        freopen(FILE".in","r",stdin);
        freopen(FILE".out","w",stdout);
        using namespace OI;
        slove();
        return 0;
    }
    View Code

    3.[haoi2008]糖果传递

    老师准备了一堆糖果, 恰好n个小朋友可以分到数目一样多的糖果. 老师要n个小朋友去拿糖果, 然后围着圆桌坐好, 第1个小朋友的左边是第n个小朋友, 其他第i个小朋友左边是第i-1个小朋友。 大家坐好后, 老师发现, 有些小朋友抢了很多的糖果, 有的小朋友只得到了一点点糖果, 甚至一颗也没有, 设第i个小朋友有ai颗糖果. 小朋友们可以选择将一些糖果给他左边的或者右边的小朋友, 通过”糖果传递”最后使得每个小朋友得到的糖果数是一样多的, 假设一颗糖果从一个小朋友传给另一个小朋友的代价是1, 问怎样传递使得所耗的总代价最小

    这题训练指南上有;

    题解:

    排除一下多余的情况,我们可以设x[i]表示i给i-1一共a[i]个糖果,a[i]小于0表示反过来给;

    于是可以列式:
    A[i]+x[i+1]-x[i]=S;

    n个方程,n个未知数,是否可以消元?

    肯定是不行的,这是一个圈上的糖果交换,前n-1个方程一定能推出第n个方程;

    而且如果能这么解出来,我们还怎么保证sum(x[i])最小;

    所以肯定不行;

    不过我们可以发现未知数太多,难以找到规律,尝试消元;

    ......

    x[3]=x[2]-(A[2]-S);

    x[2]=x[1]-(A[1]-S);

    x[1]=x[1];

    这样就可以递推用x[1]表示其他未知数;

    可以这么搞:

    x[1]=x[1];

    x[2]=x[1]-c[2];

    x[3]=x[1]-c[3];

    x[4]=x[1]-c[4];

    ......

    这样很明显看出来我们要求的答案就是数轴上一堆点,x[1]到所有点距离之和;

    这样很容易看出x[1]取所有点中位数的时候答案最小;

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<string>
     5 #include<ctime>
     6 #include<algorithm>
     7 #include<cstdlib>
     8 #include<map>
     9 #include<set>
    10 #include<vector>
    11 #include<queue>
    12 #include<cmath>
    13 using namespace std;
    14 #define FILE "dealing"
    15 #define LL long long 
    16 #define up(i,j,n) for(int i=j;i<=n;i++)
    17 #define pii pair<int,int>
    18 #define piii pair<int,pair<int,int> >
    19 template<typename T> inline bool chkmin(T &a,T b){return a>b?a=b,true:false;}
    20 template<typename T> inline bool chkmax(T &a,T b){return a<b?a=b,true:false;}
    21 namespace IO{
    22     char *fs,*ft,buf[1<<15];
    23     inline char gc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
    24     inline int read(){
    25         LL x=0;int ch=gc();bool f=0;
    26         while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();}
    27         while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();}
    28         return f?-x:x;
    29     }
    30 }using namespace IO;
    31 namespace OI{
    32     const int maxn(1010000);
    33     LL a[maxn],n,c[maxn],M,S,sum,ans=0;
    34     void slove(){
    35         n=read();
    36         up(i,1,n)a[i]=read(),M+=a[i];
    37         M/=n;
    38         up(i,1,(n-1))c[i]=c[i-1]+a[i]-M;
    39         sort(c,c+n);
    40         sum=c[n/2];
    41         up(i,0,(n-1))ans+=abs(sum-c[i]);
    42         printf("%I64d
    ",ans);
    43     }
    44 }
    45 int main(){
    46     freopen(FILE".in","r",stdin);
    47     freopen(FILE".out","w",stdout);
    48     using namespace OI;
    49     slove();
    50     return 0;
    51 }
    View Code

    注意:我们只使用前n-1个公式即可;

    给你一对数a,b,你可以任意使用(a,b), (a,-b), (-a,b), (-a,-b), (b,a), (b,-a), (-b,a), (-b,-a)这些向量,问你能不能拼出另一个向量(x,y)。
    说明:这里的拼就是使得你选出的向量之和为(x,y)

    题解:

    首先这八个向量是不必都用上的,去除那些加负号后等同的向量后剩下了四个(a,b)(a,-b)(b,a)(b,-a)。

    最后要组成的向量是(x,y),由向量运算法则设这样的式子:

    (x,y)=A(a,b)+B(a,-b)+C(b,a)+D(b,-a);

    可化成:

    x=a(A+B)+b(C+D)

    y=b(A-B)+a(C-D)

    设x1=A+B,y1=C+D,x2=A-B,y2=C-D;

    x=ax1+by1;

    y=bx2+ay2;

    然后我们就可以看出来了,如果A,B,C,D有解,

    那么x1+x2,y1+y2都是偶数;

    然后利用拓展gcd求出一组x1,y1,x2,y2,

    判断一下即可;

    细节上主要注意直接求出的一组解可能不符条件,但变形后就符合条件了

    由于判断的是奇偶,每个方程的解最多两种情况,乘起来一共四种情况,都需要判断;

    详见代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<algorithm>
    #include<cstdlib>
    #include<map>
    #include<set>
    #include<vector>
    #include<queue>
    #include<cmath>
    using namespace std;
    #define FILE "dealing"
    #define LL long long 
    #define up(i,j,n) for(int i=j;i<=n;i++)
    #define pii pair<LL,LL>
    #define piii pair<LL,pair<LL,LL> >
    template<typename T> inline bool chkmin(T &a,T b){return a>b?a=b,true:false;}
    template<typename T> inline bool chkmax(T &a,T b){return a<b?a=b,true:false;}
    namespace IO{
        char *fs,*ft,buf[1<<15];
        inline char gc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
        inline int read(){
            LL x=0;int ch=gc();bool f=0;
            while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();}
            while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();}
            return f?-x:x;
        }
    }using namespace IO;
    LL a,b,x,y,t;
    namespace OI{
        void gcd(LL a,LL b,LL& d,LL &x,LL &y){
            if(!b){d=a,x=1,y=0;return;}
            gcd(b,a%b,d,x,y);
            LL t=x;
            x=y;
            y=t-a/b*x;
        }
        char check(){
            LL d,x1,y1,x2,y2;
            gcd(a,b,d,x1,y1);
            x2=y1,y2=x1;
            a/=d,b/=d;
            if(x%d||y%d)return 'N';
            x1*=x/d,y1*=x/d,x2*=y/d,y2*=y/d;
            if(!((x1+x2)&1)&&!((y1+y2)&1))return 'Y';
            x1+=b,y1-=a;
            if(!((x1+x2)&1)&&!((y1+y2)&1))return 'Y';
            x2+=a,y2-=b;
            if(!((x1+x2)&1)&&!((y1+y2)&1))return 'Y';
            x1+=b,y1-=a;
            if(!((x1+x2)&1)&&!((y1+y2)&1))return 'Y';
            return 'N';
        }
        void slove(){
            t=read();
            while(t--){
                a=read(),b=read(),x=read(),y=read();
                printf("%c
    ",check());
            }
        }
    }
    int main(){
        freopen(FILE".in","r",stdin);
        freopen(FILE".out","w",stdout);
        using namespace OI;
        slove();
        return 0;
    }
    View Code
  • 相关阅读:
    Samba 4.0 RC3 发布
    SymmetricDS 3.1.7 发布,数据同步和复制
    Express.js 3.0 发布,Node.js 的高性能封装
    GIFLIB 5.0.1 发布,C语言的GIF处理库
    jQuery UI 1.9.1 发布
    SVN Access Manager 0.5.5.14 发布 SVN 管理工具
    DynamicReports 3.0.3 发布 Java 报表工具
    HttpComponents HttpClient 4.2.2 GA 发布
    AppCan 2.0 正式发布,推移动应用云服务
    Ruby 2.0 的新功能已经冻结
  • 原文地址:https://www.cnblogs.com/chadinblog/p/5997117.html
Copyright © 2011-2022 走看看