国庆训练10.3
the first one
tag:数论
题面
题目描述
求一个给定的圆(x2+y2=r2),在圆周上有多少个点的坐标是整数。
输入
只有一个正整数r,r<=2000 000 000
输出
整点个数
样例输入
4
样例输出
4
思路
题解
很简洁的一道题 ,这是我做过所有题里题面最短的一道,也是题意最易理解的一道。但是真正做起来就没那么容易了。这是一道数论题,首先,根据圆的对称性,只需要求处第一象限满足条件的点个数,然后*4,再加上坐标轴上四个满足条件的点就可以了。第一象限满足条件的点,暴力的做法是枚举区间[1,r)内所有的点,判断其是否满足圆的方程,但是由于r最大为2e9,显然会T;所以采取缩小枚举范围的方式进行求解。利用数论基础知识,进行推导从而达到缩小枚举范围的目的,具体推导过程如下:
x2+y2=r2
⇒
\Rightarrow
⇒ x2=r2-y2=(r+y)*(r-y);
令d=gcd((r+y),(r-y)),A=
r
+
y
d
\frac {r+y}{d}
dr+y,B=
r
−
y
d
\frac {r-y}{d}
dr−y则gcd(A,B)
≡
\equiv
≡ 1
又,x2=d2*A*B
∵
\because
∵ x2、d2为完全平方数且gcd(A,B)
≡
\equiv
≡ 1
∴
\therefore
∴A,B也为完全平方数
令A=a2,B=b2,则a2+b2=
2
∗
r
d
\frac {2*r}{d}
d2∗r
通过枚举d
∈
\in
∈[1,
2
∗
r
\sqrt{2*r}
2∗r]&d为整数,并使得2*r/d为整数,(d有两种情况:d =d or 2*r/d。分别进行讨论)然后再枚举a(设a<b),a
∈
\in
∈[1,
2
∗
r
/
2
∗
d
\sqrt{2*r/2*d}
2∗r/2∗d],求出b,在b为正整数的情况下,验证A,B是否满足:A
≠
\ne
=B&&gcd(A,B)
≡
\equiv
≡ 1,求出满足条件的点个数就欧克了。
源码
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
int main(){
ll r,r2;
cin>>r;r2=2*r;
ll a,A,B,c,d,ans=4,cnt=0;
double b;
for(d=1;d*d<=r2;d++){
if(r2%d==0){
c=d;
ll am=r/c;
for(a=1;a*a<=am;a++){
b=sqrt(1.0*r2/d-a*a);
if(b==(int)b){
A=a*a;B=b*b;
if(A!=B&&__gcd(A,B)==1) cnt++;
}
}
c=r2/d;
if(c!=d){
am=r/c;
for(a=1;a*a<=am;a++){
b=sqrt(1.0*d-a*a);
if(b==(int)b){
A=a*a;B=b*b;
if(A!=B&&__gcd(A,B)==1) cnt++;
}
}
}
}
}
ans+=4*cnt;
cout<<ans;
return 0;
}
the second one
tag:二分图匹配,匈牙利算法,递归
题面
题目描述
小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏。矩阵游戏在一个N*N黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)列交换操作:选择矩阵的任意行列,交换这两列(即交换对应格子的颜色)游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑色。对于某些关卡,小Q百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!!于是小Q决定写一个程序来判断这些关卡是否有解。
输入
第一行包含一个整数T,表示数据的组数。接下来包含T组数据,每组数据第一行为一个整数N,表示方阵的大小;接下来N行为一个N*N的01矩阵(0表示白色,1表示黑色)。
输出
输出
输出文件应包含T行。对于每一组数据,如果该关卡有解,输出一行Yes;否则输出一行No。
样例输入
2
2
0 0
0 1
3
0 0 1
0 1 0
1 0 0
样例输出
No
Yes
提示
【数据规模】
对于100%的数据,N ≤ 200
思路
题解
本来想用线性代数知识中矩阵的秩来进行求解,可是写到一半发现思路不对
这是一道二分图匹配的问题,可以用匈牙利算法进行求解。
匈牙利算法简介:
假设有n个男孩和n个女孩,其中有若干对男女互有好感(一个男or女孩可以对0个或多个女or男孩有好感),我们的任务是:进行男女匹配,看最终是否可以使得所有人都结为恋人……
我们的算法是这样的:依次给所有男生进行分配女朋友 (虽然现实中不可能这样哈哈哈),看最终是否每个男生都能抱得美人归。首先,给1号男生分配女朋友,选最近的一个与之互有好感且未有归属的女生给之,然后给2号男生分配,……出现不能给男生分配女朋友的情况后就进行调度(关键之处),调度方法:从第一个该与该男生有好感的女生开始,看是否可以给其所属男朋友更换女朋友,以把这给女生让给我们可怜的没有女朋友的男生,看其是否可以更换就又要进行调度(递归),依次对该没有女朋友的男生互有好感的女生进行调度,直到可以分配女朋友;若最终调度失败,则不能为该男生分配女朋友,整个任务失败。
我们这道题就可以套用上述算法,将行看为男生,列看为女生,a[i][j]=1表示男生i和女生j互有好感,只要能为所有行分配为1的列就可以实现主对角线全一的目标,否则不能实现。具体实现可以看代码再进行体会。
源码
/******二分图匹配,匈牙利算法,递归******/
#include<iostream>
#include<cstring>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=205;
int a[N][N],vis[N],d[N],n;
bool fun(int x){//对第x行进行分派,返回分派结果
int i;
for(i=1;i<=n;i++){
if(a[x][i]&&!vis[i]){//a[x][i]为1可分派,并且vis[i]为0即没访问过
vis[i]=1;//修改vis[i]状态,表示已访问过
if(d[i]==-1||fun(d[i])){
//精髓所在……,调度数组d,若为-1表示未分派,可用;否则调用fun函数进行调度
d[i]=x;return true;
}
}
}
return false;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;
cin>>T;
while(T--){
mem(a,0);mem(d,-1);
int i,j;
cin>>n;
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
cin>>a[i][j];
}
}
for(i=1;i<=n;i++){//对所有n行进行分派,只要有一行分派失败则整体失败
mem(vis,0);
if(!fun(i)) break;
}
i<=n?cout<<"No\n":cout<<"Yes\n";
}
return 0;
}