【Codeforces】Codeforces Round #837 (Div. 2) A-C 解题记录

184 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第18天,点击查看活动详情

【Codeforces】Codeforces Round #837 (Div. 2) A-C 解题记录

比赛链接

Dashboard - Codeforces Round #837 (Div. 2) - Codeforces

A. Hossam and Combinatorics

题目大意

给定长度为 nn 的数组 a1,a2,...,ana_1,a_2,...,a_n,求有多少个二元组 (i,j)(i,j) 满足:

  • 1i,jn1\le i,j\le n
  • iji\neq j
  • aiaj=max1p,qn(apaq)|a_i-a_j|=\max_{1\le p,q\le n}(|a_p-a_q|)

1a1,a2,...,an1051\le a_1,a_2,...,a_n\le 10^5

思路

如果所有的数都相等,那么答案就是 n×(n1)n\times(n-1)

否则,答案是所有等于最小值的数量与最大值的数量之积的两倍。

代码

#include <stdio.h>
#include <algorithm>
using namespace std;
using LL=long long;
const int N=1e6+5;
int n,m,k;
int a[N];
LL solve()
{
	scanf("%d",&n);
	for (int i=1;i<=n;++i) scanf("%d",&a[i]);
	LL cnt1,cnt2;
	cnt1=cnt2=0;
	sort(a+1,a+1+n);
	if (a[1]==a[n]) return 1ll*n*(n-1);
	for (int i=1;i<=n;++i)
	{
		cnt1+=(a[i]==a[1]);
		cnt2+=(a[i]==a[n]);
	}
	return cnt1*cnt2*2;
}
int main()
{
	int T=1;
	scanf("%d",&T);
	while (T--) printf("%lld\n",solve());
	return 0;
}

B. Hossam and Friends

题目大意

nn 个人,mm 个关系。其中第 ii 个关系 (xi,yi)(x_i,y_i) 表示 xix_iyiy_i 不是朋友。除了题目中说明的 mm 对人不是朋友之外,其他人两两之间都是朋友。

求有多少个区间 1abn1\le a\le b\le n,满足从第 aa 个人到第 bb 个人,这 ba+1b-a+1 个人两两都是朋友。

思路

我们从 11nn 枚举 bb,对每个 bb 的取值分别求有多少个合法的 aa

有多少个合法的 aa 应该怎么求呢?
合法的区间显然不能跨越任何一对给定的 (xi,yi)(x_i,y_i),所以对于第 ii 个关系式,如果 yiby_i\le b,那么合法的 aa 不能小于 xi+1x_i+1。当我们得到了 aa 可以取的最小值 ll,那么右端点是 bb 的合法区间就有 bl+1b-l+1 个

但是如果枚举输入的关系来求 aa 可以取的最小值,时间复杂度是 O(n2)O(n^2),考虑优化。

我们先对所有的关系按 yy 的值从小到大排序,将其放入队列中。一直维护一个 ll 表示 aa 最小的合法取值,最初 l=1l=1。每枚举一个新的 bb,我们都用如下方式试图对 ll 进行更新:

  1. 如果队列为空,则退出更新流程。
  2. headhead 表示队首元素,即 head.xhead.x 表示队首元素的 xx 值。
  3. 如果 head.ybhead.y \le b,则说明 aa 不应小于 head.x+1head.x+1,更新 ll,队首元素出队,跳转到步骤 1。
  4. 退出更新流程。

通过上述方式,我们保证了所有 yy 值不超过 bb 的关系都对 aa 的合法范围进行了更新。同时,每个 bb 可能的取值我们只会进行一次失败的比较,每个关系只会出队一次,所以时间复杂度为 O(n)O(n)

代码

#include <stdio.h>
#include <algorithm>
using namespace std;
using LL=long long;
const int N=1e6+5;
struct asdf{
	int l,r;
	bool operator < (const asdf a) const
	{
		return r<a.r;
	}
}a[N];
int n,m,k;
LL solve()
{
	scanf("%d",&n);
	scanf("%d",&m);
	for (int i=1;i<=m;++i) 
		if (scanf("%d%d",&a[i].l,&a[i].r),a[i].l>a[i].r) 
			swap(a[i].l,a[i].r);
	sort(a+1,a+1+m);
	LL ans=0;
	for (int i=1,j=1,l=1;i<=n;++i)
	{
		while (j<=m&&a[j].r<=i) l=max(l,a[j++].l+1);
		ans+=i-l+1;
	}
	return ans;
}
int main()
{
	int T=1;
	scanf("%d",&T);
	while (T--) printf("%lld\n",solve());
	return 0;
}

题目大意

给定 nn 个正整数 a1,a2,...,ana_1,a_2,...,a_n,判断是否存在二元组 (i,j)(i,j) 满足:

  • 1i,jn1\le i,j \le n
  • iji\neq j
  • gcd(ai,aj)1gcd(a_i,a_j)\neq 1

数据范围如下图所示。 image.png

思路

[1,109][1,\lfloor\sqrt{10^9}\rfloor] 范围内的质数筛出来,共有 m<3500m<3500 个。

遍历 1in1\le i\le n,对 aia_i 进行质因数分解,将 aia_i 的质因数去重后加入到 bb 数组里。

如果 bb 数组中有重复的数字出现,即可输出 YES

时间复杂度为 O(nm)O(nm)

代码

#include <stdio.h>
#include <algorithm>
#include <math.h>
using namespace std;
using LL=long long;
const int N=1e6+5;
const int MAXX=31622;
int v[MAXX],p[MAXX],tot;
int a[N],b[N*15];
int n,m,k;
LL solve()
{
	tot=0;
	scanf("%d",&n);
	for (int i=1;i<=n;++i) scanf("%d",&a[i]);
	for (int i=1,flag;i<=n;++i)
	{
		flag=1;
		for (int j=1;j<=m&&p[j]*p[j]<=a[i];++j)
		{
			if (a[i]%p[j]!=0) continue;
			while (a[i]%p[j]==0) a[i]/=p[j];
			b[++tot]=p[j];
		}
		if (a[i]!=1) b[++tot]=a[i];
	}
	sort(b+1,b+1+tot);
	for (int i=2;i<=tot;++i)
		if (b[i]==b[i-1]) return 1;
	return 0;
}
int main()
{
	n=MAXX-1;
	for (int i=2;i<=n;++i)
	{
		if (!v[i]) p[++tot]=i;
		for (int j=1;j<=tot&&p[j]*i<=n;++j)
		{
			v[i*p[j]]=1;
			if (i%p[j]==0) break;
		}
	}
	m=tot;
	int T=1;
	scanf("%d",&T);
	while (T--) 
		if (solve()) printf("YES\n");
		else printf("NO\n");
	return 0;
}