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
  • 相关阅读:
    autocad.net 利用linq获取矩形框内的块参照
    autocad.net 只在图纸空间遍历块的方法
    autocad.net中判断当前被激活的空间
    计划搞一个程序来应对客户的修改标记问题
    条件编译解决AutoCAD多版本问题
    初学者往往不知道怎么获得断点,请看下面的链接应该可以解决你的问题!
    2014年3月9日正式入住博客园
    学习:SpringCloud(一)
    简单使用:SpringBoot整合Redis
    Redis 使用过程中遇到的具体问题
  • 原文地址:https://www.cnblogs.com/chadinblog/p/5997117.html
Copyright © 2011-2022 走看看