广东外语外贸大学2022年程序设计新手赛の题解

题面PDF
题目链接(需要连接广外校园网)

A题 牛三的CF分数

题意

输出 n n n+ m m m

标程

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int n,m;
	cin>>n>>m;
	cout<<n+m;
	return 0;
}

B题 我俩号天梯分和权哥一样高!

题意

给三个数 a , b , c a,b,c a,b,c,问是否存在一个数是另外两个数的和。是输出”YES”,否输出"NO"

题解

读懂题意简单模拟即可

标程

#include<bits/stdc++.h> // 万能头文件
using namespace std;
long long a,b,c; //注意开long long, int类型最大范围2e9,会超

void solve()
{
	cin>>a>>b>>c;
	if(a==b+c || b==a+c || c==a+b) cout<<"YES"<<endl;
	else cout<<"NO"<<endl;
}
int main()
{
	int T=1;
	//cin>>T;
	while(T--)
	{
		solve();
	}
	return 0;
}

C题 赛博信徒不会梦见电子佛祖

题意:

求n个数的平均数,保留两位小数输出;并和a[1]比较,若a[1]大于等于平均值,输出"OVO",否则输出"QAQ"。

标程

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n;
double a[N],ans;//float型精度不够高,会在test 3出错
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		ans+=a[i];
	}
	ans/=n;
	printf("%.2lf\n",ans); //输出小数更建议printf而非cout,比较简洁
	if(a[1]>=ans) cout<<"OVO";
	else cout<<"QAQ"; 
	return 0;
}

D题 走马灯

题意

n n n 个暗着的灯,序号为 1 − n 1-n 1n。现在要循环 m m m 轮操作,对于从第 1 1 1 m m m 轮的每个轮数 i i i (1 ≤ i i i ≤ n),如果灯序号是 i i i 的倍数,则灯变为与原来相反的状态,即亮->暗或暗->亮。
m m m 轮后有多少灯亮着。

题解

双重 f o r for for循环,第一重表示 1 − m 1-m 1m的数,第二重表示灯序号

标程1

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n, m; 
bool a[N];
void solve()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(j%i==0) a[j]=!a[j];
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++) if(a[i]==1) ans++;
	cout<<ans;
}
int main()
{
	int T=1;
	//cin>>T;
	while(T--)
	{
		solve();
	}
	return 0;
}

标程2

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n, m; 
bool a[N];
void solve()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		for(int j=i;j<=n;j+=i)
		{
			a[j]=!a[j];
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++) if(a[i]==1) ans++;
	cout<<ans;
}
int main()
{
	int T=1;
	//cin>>T;
	while(T--)
	{
		solve();
	}
	return 0;
}

E题 三人打牌

题意

求一个数 c c c ,满足:
1.这个数与给定的两个数都互质;
2.在 1 ∼ 2 × 1 0 9 1\sim 2\times 10^9 12×109 中且最大。

题解

之前培训讲过判断质数的方法(再次附在下面, C + + C++ C++ 课本也有讲判断素数的方法),从 2 × 1 0 9 2\times 10^9 2×109 倒着遍历可以找到离 2 × 1 0 9 2\times10^9 2×109 最近的两个质数,分别是 1999999973 1999999973 1999999973 1999999943 1999999943 1999999943。假如说 a a a b b b 刚好是 1999999973 1999999973 1999999973 1999999943 1999999943 1999999943,那答案是 2 × 1 0 9 2\times 10^9 2×109 2 × 1 0 9 2\times 10^9 2×109 不是质数,与质数互质),要是 不是这种情况,那在 1999999943 1999999943 1999999943 及之上的数 肯定能找到一个解,满足与 a a a b b b 都互质。
大质数除了不和它的倍数的数互质,和其他都互质,这两质数太大了,在 1 ∼ 2 × 1 0 9 1\sim 2\times 10^9 12×109 范围内不会有它的倍数,所以可以认为在 1 ∼ 2 × 1 0 9 1\sim 2\times 10^9 12×109 范围内的其他数和这两大质数都互质。
所以 我们从 2 × 1 0 9 2\times 10^9 2×109 倒着遍历每个数,对遍历到的每个数,判断其是否与 a a a b b b 都互质(题面说了,两个数互质即 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1,之前培训有教过怎样求 g c d gcd gcd,而且 C + + C++ C++ 课本也有讲求 g c d gcd gcd 的方法),是的话就 break 了。因为是倒着遍历,所以第一个被找到的满足条件的数一定是最大的。
时间复杂度:每次求 g c d gcd gcd 需要 l o g N logN logN 的复杂度,上面已经说明了只需遍历不到60个数就可以找到解,所以时间复杂度是 O ( l o g N ) O(logN) O(logN)
聪明的小伙伴有没有发现样例输出是逐渐减小的呀!大胆猜一波!你就赢了!

标程

#include <bits/stdc++.h>
using namespace std;

int T,a,b;

int main(){
	cin>>T;
	while(T--){
		cin>>a>>b;
		for(int i=2e9;;--i) {
			if(__gcd(a,i)==1 && __gcd(b,i)==1) {
				cout<<i<<'\n';
				break;
			}
		}
	}
}

拓展

判断质数:

方法一:

bool isprime(int n){
    if(n<=1) return false;
    for(int i=2;i*i<=n;++i) if(n%i==0) return false;
    return true;
}

方法二(更快):

bool isprime(int n){
    if(n<=1) return false;
    if(n==2||n==3) return true;
    if(n%6!=1&&n%6!=5) return false;
    for(int i=5;i*i<=n;i+=6)if(n%i==0||n%(i+2)==0)return false;
    return true;
}

F题 KK哥哥的游戏

题意

给定 n n n个问题,使用这 n n n个问题对角色提问 m m m次,每次提问都是等概率从 n n n个问题中抽取,问是否能根据给定的问题和提问次数,判断能否找到产生智能的角色。

产生智能的角色对于同一个问题的多次提问都会有不同的回复,没有产生智能的角色对于同一个问题会有同样不变的回复。

一句话题解

思维题,若 m = 1 m=1 m=1,则输出 N o No No;若 m ≠ 1 m\ne1 m=1,则统计给出的 n n n个字符串中,有多少个不同的字符串,记为 N u m Num Num,若 N u m > m Num>m Num>m,则输出 Y e s Yes Yes,否则输出 S n o w m a n Snowman Snowman

详细题解+思路

可以注意到题目中的两个被标黑的句子,从中不难看出如果游戏中的角色被问到同样的问题两次,那么就一定能判断角色是否产生了智能。因为没智能的角色对同样问题的回复是一样的,有智能的角色则回复不一样。

那么题目其实就是,从 n n n个问题中随机抽取 m m m次,能不能必定抽取出两个同样的问题。(这里的同样的问题是指字符串相等,因为从样例2可以看出问题有可能重复)

不妨从抽取次数少的开始进行思考,若只抽取一次,那么肯定不能抽取出两个同样的问题,只有在这种情况下是输出No的,样例中也有给出;

若只抽取两次,那么则要看有多少个不同的问题,因为抽取是等概率从 n n n个问题中抽取的,如果只有一个问题,那么抽取两次,就必定会抽到同样的,输出 Y e s Yes Yes如果有两个或两个以上不同的问题,则两次抽取有可能分别抽取到不同的问题,当然也有可能抽取到同样的,那么这时候就不能保证一定抽取到同样的了,所以输出 S n o w m a n Snowman Snowman

将上面所说的情况扩展一下,假设有 N u m Num Num个不同的问题,那么考虑最坏情况,抽取的 N u m Num Num次都是不同的问题,在抽取 N u m + 1 Num+1 Num+1次的时候,抽取出来的肯定是已经抽取过的问题,那么这时候就一定能判断,输出 Y e s Yes Yes

综上,若 m = 1 m=1 m=1,那么就肯定不能判断哪个角色产生智能;若 m ≠ 1 m\ne1 m=1,此时如果抽取次数 m m m N u m Num Num多的话,则输出 Y e s Yes Yes,否则输出 S n o w m a n Snowman Snowman

扩展

这题应用到了简单的鸽巢原理,有兴趣的可以了解一下,说的大概是 n n n个鸽子, m m m个巢,如果鸽子数大于巢数的话,那么最少有 n − m n-m nm个鸽子要和其他鸽子共用一个巢。在这题中 m m m次提问相当于 m m m个鸽子, n n n个不同问题相当于 n n n个巢,如果提问次数(鸽子)大于问题数(巢)的话,那么就肯定会问到同样的问题(有鸽子要和其他鸽子共用一个巢)。

标程1

可以利用map自动去重的特性去计算不同的子串个数,复杂度 Θ ( n × l o g n ) Θ(n\times logn) Θ(n×logn)
#include <bits/stdc++.h>
using namespace std;
map<string,int> mp;
int main()
{
	int n,m;
	cin>>n>>m;
	if(m == 1)
	{
		puts("No");
		return 0;
	}
	for(int i = 1; i <= n; i++)
	{
		string s;
		cin>>s;
		mp[s] = 1;
	}
	if(m > mp.size())
		puts("Yes");
	else
		puts("Snowman");
	return 0;
}

标程2:
双重for循环暴力判断是否出现过同样的字符串,复杂度 Θ ( n 2 ) Θ(n^2) Θ(n2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+5;
string s[maxn];
int main()
{
	int n,m;
	cin>>n>>m;
	if(m == 1)
	{
		puts("No");
		return 0;
	}
	int num = 0;
	for(int i = 1; i <= n; i++)
	{
		cin>>s[i];
		int ok = 1;
		for(int j = 1; j < i; j++)
			if(s[i] == s[j])
				ok = 0;
		num += ok;
	}
	if(m > num)
		puts("Yes");
	else
		puts("Snowman");
	return 0;
}

G题 合并雪人

题解

首先我们看题,可以知道我们合并的两个雪人不是连续的,这就可以否决这题用区间DP来做(同时也可看数据范围就知道不能区间DP)
因此,这道题其实是一道贪心算法问题,我们每次都挑当前重量最小的两个雪人合并,最终我们所得到耗费的体力就一定是最小的
那么我们就需要在枚举 n − 1 n-1 n1次( n n n个雪人要合并 n − 1 n-1 n1次才能变成一个雪人)的过程中,每次都能够得到最小值和次小值,将其合并。
那么,我们的做法有:

1、利用优先队列维护大根堆来做 (时间复杂度 n 2 l o g ( n ) n^2log(n) n2log(n)

标程1
#include <bits/stdc++.h>
using namespace std;
typedef long long ll; //定义long long 为 ll,为了方便简写
const int maxn = 1e5+10;
int n;
ll ans;
priority_queue<ll,vector<ll>,greater<ll> > q; //优先队列,可以实现自动将队列中的元素从小到大排列
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	//以上两行取消缓冲区解绑,加快输入输出
	cin >> n;
	for(int i=1; i<=n; i++){
		ll x;
		cin >> x;
		q.push(x);
	}
	for(int i=1; i<n; i++){
		ll a=q.top(); //取队头
		q.pop();  //出队
		ll b=q.top();
		q.pop();
		a+=b;
		ans+=a;
		q.push(a); //运算完之后放进队列,然后与剩下的雪人进行排序
	}
	cout << ans;
	return 0;
}

2、利用sort排序做 (时间复杂度 n^2log(n))

标程2
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;  //定义long long 为 ll,为了方便简写
const int maxn = 1e5+10;
int n;
ll a[maxn],ans; 
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	//以上两行取消缓冲区解绑,加快输入输出
	cin >> n;
	for(int i=1; i<=n; i++) cin >> a[i];
	for(int i=1; i<n; i++){
		sort(a+i,a+1+n); //将雪人数量从小到大排序
		ans+=a[i];
		ans+=a[i+1];
		a[i+1]+=a[i];//每次把当前雪人合并到下一个雪人中
	}
	cout << ans;
	return 0;
}

H题 wyxz

题意

给定一个化学方程式,一开始有一个装水的烧杯,有 k k k次操作,每次往烧杯里添加某一种反应物溶液,求最终的烧杯内所有物质的浓度之和

题解

思维+模拟
如果直接每次操作后都判断是否会发生反应,那么最坏情况下复杂度是 1 e 6 ∗ 1 e 6 = 1 e 12 1e6*1e6=1e12 1e61e6=1e12,必然超时
可以直接每次操作后都把反应物的量进行增加, k k k次操作之后,再直接统一按比例进行操作,得到最终状态,求出其物质的量之和,除以总体积即可,详情看代码

标程

#include<bits/stdc++.h>

using namespace std;

const int maxn=1e5+5;

int n,m,k;
int x[maxn],y[maxn];
double num[maxn];

void solve(){
	double N=0,V;
	cin>>n>>m>>V>>k;
	
	//记得每个样例都要初始化必须得初始化的数组
	//并且不能用memset,memset会把整个数组全都初始化了(超时)
	//而实际上只需要初始化1-n这部分,故用for循环来初始化 
	//读入n之后再初始化 
	for(int i=1;i<=n;i++) num[i]=0;
	
	
	for(int i=1;i<=n;i++) cin>>x[i];
	for(int i=1;i<=m;i++) cin>>y[i];
	for(int i=1;i<=k;i++){
		double v,c;
		int id;
		cin>>v>>c>>id;
		V+=v;
		num[id]+=c*v;//反应物id的物质的量增加了c*v
	}
	double unit=1e8;//单位系数消耗/生成的物质的量 (单位系数的消耗量)
	for(int i=1;i<=n;i++){
		//取最小比例使得恰好某一个反应物耗尽(恰反应停止),而其他反应物的物质的量都≥0 
		unit=min(unit,num[i]/x[i]);
	}
	for(int i=1;i<=n;i++){
		//计算反应物的消耗量
		double cost=x[i]*unit;
		N+=num[i]-cost;//加上剩余量
	}
	for(int i=1;i<=m;i++){
		N+=y[i]*unit;//加上生成物的量
	}
	printf("%.3lf\n",N/V);
}

int main(){
	int T=1;
	cin>>T;
	while(T--){
		solve();
	} 
	return 0;
}

I题 决斗周四

题解

这题是一道很经典的巴什博奕。
巴什博奕: n n n个石子,每人一次最少拿 1 1 1个,最多拿 m m m个,最后没石子可以拿的人视为输家
结论:当 n n n% ( m + 1 ) (m+1) (m+1)== 0 0 0时,后手必胜,否则先手必胜。

我们可以把 m + 1 m+1 m+1看成一个后手必胜的状态,因为对于 m + 1 m+1 m+1个石子,无论先手拿多少个石子,后手都能一次拿完,这时轮到先手再拿的时候,就没有石子可以拿了。
所以如果 n n n k k k ( m + 1 ) (m+1) (m+1)组成,换句话说就是 n n n% ( m + 1 ) (m+1) (m+1) = = == == 0 0 0时,后手必胜。
如果不存在 n n n% ( m + 1 ) (m+1) (m+1) ! = != != 0 0 0,那先手就可以先把 n n n% ( m + 1 ) (m+1) (m+1)的余数拿完,然后扔给后手一个上述的局面,这时后手就必败了,先手存在必胜态。

标程

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,m;
	cin >> n >> m;
	if(n % (m+1) == 0) cout << "ni hai shi qu zuo he suan ba";
	else cout << "V me 50";
	return 0;
}

J题 Kart的幸运数字

题意

求出以 a a a为首项, q q q为公比的等比数列的第 k k k项的个位数字,由于 k k k 1 e 9 1e9 1e9级别,故使用暴力枚举或 p o w pow pow函数直接求出第 k k k项数字会导致超时。本题正解包含找规律(思维)和快速幂取模(算法)两种方法。

题解

①【思维做法】

由乘法的性质可知两数乘积的个位数不受高位数字影响,因此首先可以只取 a a a q q q的个位数来进行计算;通过简单推理可以得出相同公比连乘得到的个位数会形成一个循环节,且循环节都不超过 4 4 4个,因此可以先存取对应循环节的长度,即可将庞大的 k k k缩小为个位级别的数,最后直接根据第 k k k项在所在循环节中的位置得出结果。

标程1
#include <bits/stdc++.h>
using namespace std;

int a,q,k;
bool vis[10]; 
//vis数组记录0~9是否出现过,如果出现过,说明已得到完整的一个循环节
int num[10]; //num数组记录当前数列的循环节

int main()
{
	cin >>a >>q >>k;

	//只对个位数进行操作
	a=a%10;
	q=q%10; 

	//若公比为1,则数列每一项都等于首项;若k为1,则直接输出首项
	if(q==1||k==1) 
	{
		cout <<a;
		return 0;
	}

	int cnt=0; //记录循环节长度
	int x=(a*q)%10; //循环节从第二项开始
	while(!vis[x]) //未出现相同数字则继续增加循环节元素
	{
		vis[x]=1; //标记循环节中已经出现过的元素
		cnt++; //更新循环节长度
		num[cnt]=x; //记录循环节的每个元素
		x=(x*q)%10; //寻找循环节下一项的个位数
	}
	int mod=(k-1)%cnt; //查找k在对应循环节中的位置
	if(mod==0) mod=cnt; //位置从1到长度cnt
	cout <<num[mod]; //输出第k项的个位数
	return 0;
}
②【算法做法】

由乘法的性质可知两数乘积的个位数不受高位数字影响,因此计算时只需要考虑 a a a q q q的个位数,即每次计算都对 10 10 10取模。此时可以考虑运用快速幂算法,用 O ( l o g ( k ) ) O(log(k)) O(log(k))的时间复杂度直接求出 q q q k − 1 k-1 k1次方的个位数,乘首项 a a a后再对 10 10 10取模即可得到结果。

标程2
#include <bits/stdc++.h>
using namespace std;
#define ll long long //在程序用ll代替long long,方便书写

/*快速幂算法模板,求出x的y次方对p取模的结果。因为乘法过程中可能会超出int范围,因此取模之前要转为long long类型。*/
ll qmi(ll x, int y, int p) 
{
	ll res = 1;
	while(y!=0)
	{
		if(y%2==1) res=(res*x)%p;
		x=(x*x)%p;
		y/=2;
	}
	return res;
}

int a,q,k;

int main()
{
	cin >>a >>q >>k;
	int ans=(a*qmi(q,k-1,10))%10; //等比数列第k项的个位数
	cout <<ans;
	return 0;
}


K题 开摆!

题意

有张大小为 a × b a × b a×b 的空桌子和 n n n 张矩形桌垫,要求在上面摆两张桌垫,且不重叠不伸出桌子。
若可以摆下,输出桌垫最大总面积;否则输出"kaibai!"

题解

暴力做法:
1.双重 f o r for for循环枚举选哪两张桌垫。
2.第一张桌垫放桌子左上角,接下来,第二张桌垫只有两种情况:要么放在第一张的下边,要么放在第一张的右边。
3.桌垫本身可以旋转,因此两张桌垫还要分旋转和不旋转的情况。
4.合法就更新答案最大值。
5.答案与初值不同,证明有可以摆下的方案,输出答案;相同输出"kaibai!"

标程

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
struct node 
{
	int x, y, s;
} c[N];
int a, b, n;
void solve() 
{
	cin>>a>>b;
	cin>>n;
	for(int i=1; i<=n; i++) 
	{
		cin>>c[i].x>>c[i].y;
		c[i].s=c[i].x*c[i].y; //预先存好每张桌垫面积
	}
	int ans=-1;
	for(int i=1;i<=n-1;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			if(max(c[i].x,c[j].x)<=a && (c[i].y+c[j].y)<=b) ans=max(ans, c[i].s+c[j].s); //j放i下边,i不旋转,j不旋转 
			if(max(c[i].y,c[j].y)<=a && (c[i].x+c[j].x)<=b) ans=max(ans, c[i].s+c[j].s); //j放i下边,i旋转,j旋转 
			if(max(c[i].x,c[j].y)<=a && (c[i].y+c[j].x)<=b) ans=max(ans, c[i].s+c[j].s); //j放i下边,i不旋转,j旋转 
			if(max(c[i].y,c[j].x)<=a && (c[i].x+c[j].y)<=b) ans=max(ans, c[i].s+c[j].s); //j放i下边,i旋转,j不旋转 
			if(max(c[i].x,c[j].x)<=b && (c[i].y+c[j].y)<=a) ans=max(ans, c[i].s+c[j].s); //j放i右边,i不旋转,j不旋转
			if(max(c[i].y,c[j].y)<=b && (c[i].x+c[j].x)<=a) ans=max(ans, c[i].s+c[j].s); //j放i右边,i旋转,j旋转
			if(max(c[i].x,c[j].y)<=b && (c[i].y+c[j].x)<=a) ans=max(ans, c[i].s+c[j].s); //j放i右边,i不旋转,j旋转 
			if(max(c[i].y,c[j].x)<=b && (c[i].x+c[j].y)<=a) ans=max(ans, c[i].s+c[j].s); //j放i右边,i旋转,j不旋转
		}
	}
	if(ans==-1) cout<<"kaibai!";
	else cout<<ans;
	cout<<endl; 
}
int main() 
{
	int T=1;
	//cin>>T;
	while(T--)
	{
		solve();
	}
	return 0;
}

L题 雪人的重叠面积

题解

假设半径小的圆为 c 1 c_1 c1,半径大的圆为 c 2 c_2 c2 c 1 c_1 c1 的半径为 r 1 r_1 r1,圆心坐标 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) c 2 c_2 c2 的半径为 r 2 r_2 r2 ,圆心坐标 ( x 2 , y 2 ) (x_2,y_2) (x2,y2) d d d 为两圆圆心连线的长度,相交面积为 S S S

根据两点距离公式,容易得到
d = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 ) d=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2)} d=(x1x2)2+(y1y2)2)

  • Case 1: r 1 + r 2 ≤ d r_1+r_2\le d r1+r2d

那么两圆相离,相交面积 S = 0 S=0 S=0

  • Case 2: ∣ r 2 − r 1 ∣ ≥ d |r_2-r_1|\ge d r2r1d

那么半径小的圆内含半径大的圆,那么相交面积为小圆的面积 S = π × r m i n × r m i n S=\pi \times r_{min}\times r_{min} S=π×rmin×rmin

  • Case 3: 不属于以上两种的情况,两圆相交,不失一般性,考虑两个普通相交圆

那么两圆相交,连接小圆的圆心与两个圆的交点,连接大圆的圆心和两个圆的交点,可以发现形成的图形被两个圆心的连线平分成 2 2 2 个全等三角形。

  • 由小圆圆心和交点所连两条线(长度为半径)以及在大圆之内的弧所形成的扇形为S1
  • 由大圆圆心和交点所连两条线(长度为半径)以及在小圆之内的弧所形成的扇形为S2
  • 由小圆圆心和交点所连两条线以及由大圆圆心和交点所连两条线所形成的四边形的面积为S3

可见相交面积 S = S 1 + S 2 − S 3 S=S_1+S_2-S_3 S=S1+S2S3

要求出扇形的面积,要知道扇形的圆心角,考虑在两个三角形中,使用余弦定理
a n g l e 1 = arccos ⁡ ( r 1 2 + d 2 − r 2 2 2 r 1 d ) a n g l e 2 = arccos ⁡ ( r 2 2 + d 2 − r 1 2 2 r 2 d ) S 1 = π × r 1 × r 1 × 2 × a n g l e 1 2 π = a n g l e 1 × r 1 × r 1 \mathrm{angle}_1=\arccos(\frac{r_1^2+d^2-r_2^2}{2r_1d})\\ \mathrm{angle}_2=\arccos(\frac{r_2^2+d^2-r_1^2}{2r_2d})\\ S_1=\frac{\pi\times r_1\times r_1\times 2 \times \mathrm{angle}_1}{ 2\pi}=\mathrm{angle}_1\times r_1\times r_1 angle1=arccos(2r1dr12+d2r22)angle2=arccos(2r2dr22+d2r12)S1=2ππ×r1×r1×2×angle1=angle1×r1×r1
同理
S 2 = a n g l e 2 × r 2 × r 2 S_2=\mathrm{angle}_2\times r_2\times r_2 S2=angle2×r2×r2
S 3 S_3 S3 为一个三角形面积的 2 2 2
S 3 = 2 r 1 d sin ⁡ a 1 2 = r 1 d sin ⁡ a 1 S_3=\frac{2r_1d\sin a_1}{2}=r_1d \sin a_1 S3=22r1dsina1=r1dsina1

标程

#include <iostream>
#include <cmath>
using namespace std;


double PI = acos(-1.0);

double area(double x1, double y1, double r1, double x2, double y2, double r2) {
	double d = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
	if (d >= r1 + r2)
		return 0;
	else if (fabs(r1 - r2) >= d) {
		if (r1 > r2)
			return PI * r2 * r2;
		else
			return PI * r1 * r1;
	}
	else {
		//余弦定理求扇形圆心角
		double a1 = 2 * acos((r1 * r1 + d * d - r2 * r2) / 2 / r1 / d);
		double a2 = 2 * acos((r2 * r2 + d * d - r1 * r1) / 2 / r2 / d);
		//两个扇形面积和减去四边形的面积即为相交区域面积
		//四边形面积再转化为两个三角形的面积之和来计算
		double ans = r1 * r1 * a1 / 2 + r2 * r2 * a2 / 2 - r1 * r1 * sin(a1) / 2 - r2 * r2 * sin(a2) / 2;
		return ans;
	}
}
int main() {
	double x1, y1, r1, x2, y2, r2;
	
	cin >> x1 >> y1 >> r1 >> x2 >> y2 >> r2;

	printf("%.3lf\n", area(x1, y1, r1, x2, y2, r2));

	return 0;
}

捧着一颗心来,不带半根草去
					————致敬负责本场比赛,不辞辛苦无私奉献的老师、出题人和验题人们
  • 30
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值