题目描述
难度分:1300
输入n(2≤n≤2×105)和长为n的数组a(1≤a[i]≤106)。
对于数组b,如果b中存在一个数x,使得x=b中其余元素之和,则称b为「好数组」。
如果删除a[i]可以使剩余元素组成好数组,则称i为「好下标」。
输出a的好下标的个数,以及所有好下标(任意顺序)。注意下标从1开始。
输入样例1
5
2 5 1 2 2
输出样例1
3
4 1 5
输入样例2
4
8 3 5 2
输出样例2
2
1 4
输入样例3
5
2 1 2 4 3
输出样例3
0
算法
有序表
先求出整个数组的累加和tot,并将二元组(a[i],i)放入到一个有序集合中。然后遍历i∈[1,n],假设此时要将a[i]删除,那么剩余数组的累加和就会变为tot−a[i],将(a[i],i)从有序集合中删除,并从中选出元素值最大的那个二元组,获得此时剩余数组中的最大值mx。只要满足tot−a[i]=2mx,说明剩余数组中存在一个元素的值为mx,而剩余数组除去这个元素后其他元素的累加和也为mx,剩余数组是一个好数组,i是一个好下标。
然后继续考虑后面的i,但是需要恢复现场,将二元组(a[i],i)重新插回到有序集合中。
复杂度分析
时间复杂度
遍历i∈[1,n]的时间复杂度为O(n),对于每个i,存在有序表的插入和删除操作,时间复杂度是O(log2n)的。因此,整个算法的时间复杂度为O(nlog2n)。
空间复杂度
额外空间主要是有序集合,它的内部在整个算法过程中都存放着O(n)数量级别的二元组,因此算法的额外空间复杂度为O(n)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int n, a[N];
int main() {
scanf("%d", &n);
set<pair<int, int>> st;
long long tot = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
st.insert({a[i], i});
tot += a[i];
}
vector<int> pos;
for(int i = 1; i <= n; i++) {
st.erase({a[i], i});
int mx = st.rbegin()->first;
if(tot - a[i] == 2*mx) {
pos.push_back(i);
}
st.insert({a[i], i});
}
printf("%d\n", (int)pos.size());
for(int x: pos) printf("%d ", x);
return 0;
}