博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
数论某些题目
阅读量:5278 次
发布时间:2019-06-14

本文共 7521 字,大约阅读时间需要 25 分钟。

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
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
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
17 #define piii pair
>18 template
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\n",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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
#define piii pair
>template
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\n",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
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
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
18 #define piii pair
>19 template
inline bool chkmin(T &a,T b){ return a>b?a=b,true:false;}20 template
inline bool chkmax(T &a,T b){ return a
'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\n",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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
#define piii pair
>template
inline bool chkmin(T &a,T b){ return a>b?a=b,true:false;}template
inline bool chkmax(T &a,T b){ return a
'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\n",check()); } }}int main(){ freopen(FILE".in","r",stdin); freopen(FILE".out","w",stdout); using namespace OI; slove(); return 0;}
View Code

 

转载于:https://www.cnblogs.com/chadinblog/p/5997117.html

你可能感兴趣的文章
Linux编程简介——gcc
查看>>
2019年春季学期第四周作业
查看>>
MVC4.0 利用IActionFilter实现简单的后台操作日志功能
查看>>
rotate the clock
查看>>
bugku 变量
查看>>
数据库01 /Mysql初识以及基本命令操作
查看>>
数据库02 /MySQL基础数据类型以及多表之间建立联系
查看>>
Python并发编程04/多线程
查看>>
CF461B Appleman and Tree
查看>>
CF219D Choosing Capital for Treeland
查看>>
杂七杂八的小笔记本
查看>>
51Nod1353 树
查看>>
CF1215E Marbles
查看>>
BZOJ2339 HNOI2011卡农(动态规划+组合数学)
查看>>
octave基本操作
查看>>
axure学习点
查看>>
WPF文本框只允许输入数字[转]
查看>>
dom4j 通用解析器,解析成List<Map<String,Object>>
查看>>
第一个项目--用bootstrap实现美工设计的首页
查看>>
使用XML传递数据
查看>>