题目链接
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;
}