Educational Codeforces Round 173 (Rated for Div. 2) C&D题解

题目链接

https://codeforces.com/contest/2043

C题

题目描述

给定一个包含 n 个整数的数组 a,其中除了最多一个元素外,其余元素都等于 −1 或 1。剩余的元素 x 满足 −109≤x≤109。

找出数组 a 的所有可能的子数组和(包括空子数组,其和定义为 0)。换句话说,找出所有整数 x,使得数组 a 至少有一个子数组(可能为空)的和等于 x。子数组是数组的一个连续子段。

按升序输出这些和。每个和应仅输出一次,即使它可以通过多个子数组实现。

输入格式

第一行包含一个整数 t(1≤t≤104)——测试用例的数量。然后跟随 t 个测试用例。

每个测试用例包含两行:

  • 第一行包含一个整数 n(1≤n≤2⋅105)——数组的大小。
  • 第二行包含 n 个整数 a1​,a2​,…,an​(−109≤ai​≤109)——数组 a 的元素。在数组 a 中,最多有一个元素既不是 1 也不是 −1。

输入的附加限制:所有测试用例的 n 的总和不超过 2⋅105。

输出格式

对于每个测试用例,输出两行:

  • 第一行,输出一个整数——不同子数组和的数量。
  • 第二行,按升序输出这些和。

每个和应仅输出一次,即使它由多个子数组产生。

样例 #1

样例输入 #1

8
-1 0 1 2 9 10 11 12 
6
-5 -4 -3 -2 -1 0 
4
-1 0 1 2 
4
0 1 7 8 
6
-1 0 1 3 4 5
样例输出 #1
5
5
1 -1 10 1 1
5
-1 -1 -1 -1 -1
2
-1 2
2
7 1
3
1 4 -1
简单理解

给定一个数组,其中大部分元素为 −1 或 1,最多有一个元素可以是任意整数(范围在 −109 到 109 之间)。需要找出所有可能的子数组和(包括空子数组的和,其和为 0),并输出这些不同的和及其数量,要求和按升序排列。

最大最小值是dp问题

遍历这个区间加上x看看有没有新的值加入即可

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
map<int,int>mp;
int n;int a[N];
int l[N],r[N];//分别用于存储以当前元素结尾的子数组的最大和和最小和
void solve()
{
	mp.clear();int ind=-1;int ans=0;//一个map,用于存储不同的和及其出现的次数
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		l[i]=r[i]=0;
		scanf("%d",&a[i]);
		if(a[i]<-1||a[i]>1||a[i]==0)ind=i;
	}
	int aa=0,b=0,c=0,d=0;
	if(ind==-1)ind=n+1;
	for(int i=1;i<ind;i++)
	{
		l[i]=max(a[i],l[i-1]+a[i]);
		r[i]=min(a[i],r[i-1]+a[i]);
		aa=max(aa,l[i]);
		b=min(b,r[i]);
	}
	for(int i=ind+1;i<=n;i++)
	{
		l[i]=max(a[i],l[i-1]+a[i]);
		r[i]=min(a[i],r[i-1]+a[i]);
		c=max(c,l[i]);
		d=min(d,r[i]);
	}
//使用两个循环分别计算特殊元素左侧和右侧子数组的最大和aa、c和最小和b、d
	b=min(b,d);aa=max(aa,c);ans=aa-b+1;//更新b和aa,并计算不考虑特殊元素时的不同和的数量
	for(int i=b;i<=aa;i++)mp[i]++;
	if(ind!=n+1)
	{
		if(!mp[a[ind]])
		{
			mp[a[ind]]++;
			ans++;
		}
		int sum=0;
		int x1=0,x2=0,y1=0,y2=0;
		for(int i=ind+1;i<=n;i++)
		{
			sum+=a[i];x1=min(x1,sum);x2=max(x2,sum);
		}
		sum=0;
		for(int i=ind-1;i;i--)
		{
			sum+=a[i];y1=min(y1,sum);y2=max(y2,sum);
		}
		int ll=x1+y1,rr=x2+y2;
		for(int i=ll;i<=rr;i++)
		{
			if(!mp[a[ind]+i])
			{
				mp[a[ind]+i]++;ans++;
			}
		}
	}
	cout<<ans<<endl;
	for(auto x:mp)
	{
		cout<<x.first<<' ';
	}
	cout<<endl;
}
int main()
{
	int t;cin>>t;
	while(t--)solve();
}

D题

题目描述

给定三个整数 l,r 和 G,找出两个整数 A 和 B(满足 l≤A≤B≤r)使得它们的最大公约数(GCD)等于 G,并且它们的距离 ∣A−B∣ 最大化。

如果存在多个这样的数对,选择 A 最小的那个。如果不存在这样的数对,则输出 "-1 -1"。

输入格式

第一行包含一个整数 t(1≤t≤103)——测试用例的数量。接下来是 t 个测试用例。

每个测试用例包含一行,包含三个整数 l,r,G(1≤l≤r≤1018;1≤G≤1018)——范围边界和所需的最大公约数。

输出格式

对于每个测试用例,输出两个整数 A 和 B——问题的解,或者如果不存在这样的数对,则输出 "-1 -1"。

样例

样例输入

4
4 8 2
4 8 3
4 8 4
5 7 6

样例输出

4 6
-1 -1
4 8
6 6

简单理解

在给定的整数范围 [l,r] 内,找出两个整数 A 和 B,使得它们的最大公约数(GCD)等于 G,并且这两个数的差值 ∣A−B∣ 最大。如果有多个满足条件的数对,选择 A 最小的那个数对。如果找不到这样的数对,则输出 "-1 -1"。

这个问题要求我们在给定的范围内,找到满足特定GCD条件的两个整数,同时要求这两个整数的差值尽可能大。如果找不到这样的整数对,就输出特定的标识("-1 -1")。

#include<bits/stdc++.h> 
using namespace std;
// 计算两个长整数的最大公约数
long long gcd(long long a, long long b)
{
	return b ? gcd(b, a % b) : a; // 递归计算最大公约数,直到 b 为 0
}

// 解决单个测试用例
void solve()
{
	long long x, y, g; // x 和 y 是区间的左右端点,g 是 A 和 B 必须是其倍数的数
	cin >> x >> y >> g;

	// 将 x 调整为不小于 x 的最小的 g 的倍数,将 y 调整为不大于 y 的最大的 g 的倍数
	long long l = (x + g - 1) / g, r = y / g;
	
	// 从最大的可能差值开始递减到 0,尝试找到满足条件的 A 和 B
	for (long long len = r - l; len >= 0; len--)
	{
		for (long long i = l; i + len <= r; i++)
		{
			// 检查 A = g * i 和 B = g * (i + len) 是否互质
			if (gcd(i, i + len) == 1)
			{
				// 如果互质,则输出 A 和 B,并结束当前测试用例的处理
				cout << g * i << ' ' << g * (i + len) << endl;
				return;
			}
		}
	}
	
	// 如果没有找到满足条件的 A 和 B,则输出 -1 -1
	cout << "-1 -1" << endl;
}

int main()
{
	int t;
	cin >> t; 
	while (t--)
		solve();
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值