XDU2021校赛

这篇博客详细解析了三道编程竞赛题目,包括序列的奇偶性判断、最大公因数计算以及区间图与树的性质检验。通过C++代码展示了如何高效解决这些问题,涉及并查集、排序和区间查找等算法。此外,还提到了其他可能的解题思路和数据处理注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A 序列那么长

题目大意

给定一个序列,求前缀和/前缀积的奇偶性

分析

水题

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
int n;
int x;
int sum, mul=1;
int main()
{
	int n;
	cin>>n;
	for (int i = 1; i <= n; i++)
	{
		cin>>x;
		if (x < 0)
			x = -x;
		x &= 1;
		sum = (sum+x)&1;
		mul &= x;
		cout<<sum<<mul<<endl;
	}
	return 0;
}

B gcd

题目大意

给定一个序列 { a n } \{a_n\} {an},给每个数都加上一个整数 x x x,求 max ⁡ gcd ⁡ { a n } \max\gcd\{a_n\} maxgcd{an}

分析

设能达到的 gcd ⁡ \gcd gcd g g g,那么 { a n } \{a_n\} {an}在模 g g g意义下显然必须相等,于是各元素差的最大公因数即可。特判一下若整个数列相等,那么结果为 inf ⁡ \inf inf

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
int gcd(int x, int y)
{
	if (!x)
		return y;
	if (!y)
		return x;
	if (x < y)
		return gcd(y, x);
	return gcd(y, x%y);
}
#define N 1000006
int n;
int arr[N];
int main()
{
	cin.sync_with_stdio(false);
	cin.tie(nullptr);
	cin>>n;
	bool inf = true;
	for (int i = 1; i <= n; i++)
	{
		cin>>arr[i];
		if (arr[i] != arr[1])
			inf = false;
	}
	if (inf)
	{
		cout<<"inf";
		return 0;
	}
	sort(arr+1, arr+n+1);
	int g;
	for (int i = 1; i < n; i++)
	{
		arr[i] = arr[i+1] - arr[i];
		if (i==1)
			g = arr[i];
		else
			g = gcd(g, arr[i]);
	}
	cout<<g;
	return 0;
}

C 区间图与树

题目大意

给定 n n n个顶点,每个顶点是一个区间 [ i , j ] [i,j] [i,j],两顶点有边相连    ⟺    \iff 两区间相交。问图是否是一个树

分析

首先不考虑数据出锅了导致这道题有特别简单的错误解法可以AC的情况。做的时候卡了好久,首先并查集是肯定的,但是时间复杂度 O ( n 2 ) O(n^2) O(n2)无情TLE了,然后考场上想到了用线段树去优化查找,由于坐标范围过大又需要上离散化——这当然是一种写法,只不过官方题解也说了比较麻烦。

实际上做的时候没意识到的是,先对区间排序,由于同侧端点的单调性,在对区间 x x x找它的相交区间时,这些区间一定是相邻的,这样保证(几乎)所有的访问一定是先找到相交区间。于是就可以保证 n − 1 n-1 n1次成功访问就可以建立 n − 1 n-1 n1条边,这样一旦建立 n n n条边就可以直接判定 N O NO NO,保证两重循环的时间复杂度是 O ( n ) O(n) O(n)

然后数据的问题是没判森林:实际上两区间相交的话判断一下它们是否已经同属一个集合即可。在恰有 n − 1 n-1 n1条边时,由于导致森林的不连通性的边一定多余存在于某个集合这种内部,这种不合法条件和森林是等价的。

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 1000006
typedef pair<int, int> P;
P arr[N];
int fa[N];
int n, m;
int father(int x)
{
	if (!fa[x])
		return fa[x] = x;
	if (fa[x] == x)
		return x;
	else
		return fa[x] = father(fa[x]);
}
void uni(int x, int y)
{
	fa[father(x)] = father(y);
}
bool xj(int x, int y)
{
	if (arr[x].first <= arr[y].first && arr[x].second >= arr[y].first
	    || arr[y].first <= arr[x].first && arr[y].second >= arr[x].first)
		return true;
	return false;
}
int main()
{
	cin>>n;
	for (int i = 1; i <= n; i++)
	{
		int l, r;
		cin>>l>>r;
		arr[i] = make_pair(l, r);
	}
	sort(arr+1, arr+n+1, [](const P &lhs, const P &rhs) -> bool{
		return rhs.second < lhs.second;
	});
	for (int i = 2; i <= n && m < n; i++)
	{
		for (int j = i-1; j >= 0; j--)
			if (xj(i, j))
			{
				if (father(i) == father(j))
				{
					cout<<"NO";
					return 0;
				}
				else
				{
					uni(i, j);
					m++;
				}
			}
			else
				break;
	}
	if (m == n-1)
		cout<<"YES";
	else
		cout<<"NO";
	return 0;
}

做法2

本题做法还是挺多的,赛后说扫描线可以过,只不过赛场上看见 a i < 1 0 9 a_i<10^9 ai<109有点吓人就没敢写。

其余题目待补

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zclll123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值