比赛链接:
https://codeforces.com/contest/1698
C. 3SUM Closure
题意:
给定一个序列 \(a\),判断对于任意一个 \(1 <= i < j < k <= n\),是否存在一个 \(1 <= l <= n\),使得 \(a[i] + a[j] + a[k] = a[l]\)。
思路:
首先如果有三个正数及以上的正数,那么选择其中最大的三个,它们的和肯定不在序列中,同理得到负数也不会超过三个。
对于 0 而言,容易知道,最多只会有两个 0 对结果产生影响(两个 0 或一个 0,与正数或者负数组合一下)。
这样算下来最多只有 6 个数对结果产生影响,暴力枚举一下即可。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
LL n;
cin >> n;
vector <LL> a(n), b;
LL pos = 0, neg = 0, cnt = 0;
for (int i = 0; i < n; i ++ ){
cin >> a[i];
if (a[i] > 0){
pos ++ ;
b.push_back(a[i]);
}
else if (a[i] < 0){
neg ++ ;
b.push_back(a[i]);
}
else{
cnt ++ ;
}
}
if (pos >= 3 || neg >= 3){
cout << "NO\n";
}
else{
cnt = min(cnt, 2LL);
for (int i = 0; i < cnt; i ++ ){
b.push_back(0);
}
LL m = b.size();
map <LL, LL> mp;
for (int i = 0; i < m; i ++ ){
mp[b[i]] = 1;
}
for (int i = 0; i < m; i ++ ){
for (int j = 0; j < m; j ++ ){
for (int k = 0; k < m; k ++ ){
if (i == j || j == k || i == k) continue;
LL t = b[i] + b[j] + b[k];
if (mp[t] == 0){
cout << "NO\n";
return;
}
}
}
}
cout << "YES\n";
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
LL T;
cin >> T;
while (T -- )
solve();
return 0;
}
D. Fixed Point Guessing
题意:
给定一个排列,现在对其中的 \(n - 1\) 个数进行两两交换(\(n\) 为奇数),即只有一个数的位置是不变的。
可以最多进行 15 次询问去找出这个数,每次询问输出 \(? l r\),返回序列打乱后,\(l\) 到 \(r\) 的元素进行升序排序之后的子序列。
思路:
通过数据范围可以推测出二分。
对于 \(l\) 到 \(r\) 这个排序好的序列的元素,如果它在区间范围内,说明它是内部的交换,否则它是外部的交换。
如果内部的交换次数为奇数,说明有一个元素的位置没有发生变换(其它内部元素两两交换的)。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
bool ask(LL a, LL b){
cout << "? " << a << " " << b << "\n";
vector <LL> t(b - a + 1);
for (int i = 0; i < b - a + 1; i ++ ){
cin >> t[i];
}
LL cnt = 0;
for (int i = 0; i < b - a + 1; i ++ ){
if (a <= t[i] && t[i] <= b){
cnt ++ ;
}
}
return cnt % 2;
}
void solve(){
LL n;
cin >> n;
LL l = 1, r = n;
while (l < r){
LL mid = (l + r) >> 1;
if (ask(l, mid)){
r = mid;
}
else{
l = mid + 1;
}
}
cout << "! " << l << "\n";
}
int main(){
LL T;
cin >> T;
while (T -- )
solve();
return 0;
}