题意:给出数组a[1...n],b[1...n],挑选不超过n/2+1个下标,使得挑选出的下标对应的数之和大于没被挑选出来的。
题解:
nlogn做法:
a数组从大到小排序。以下对排序之后的数组进行操作。(所提到的下标都是排序之后的下标)
首先选取下标1。如果n是偶数,下标n也要取。
对于剩下的下标,2与3选取(b[2]>b[3])?2:3,4与5....类推
对于b数组,这样的选取肯定成立。
对于a数组,a[1]>=max(a[2],a[3]),a[2与3中选取的数]>=max(a[4],a[5])...因此a数组也成立。
on做法:
预处理a[i+n]=a[i]。选取长度为n/2+1,且连续的一段。
证明1:
首先证明:在任意一个数组中,满足[长度为n/2+1,且连续的一段,和大于剩下的数]条件的有n/2+1个。
枚举开头在[1,n/2]中长度为n/2+1的段,对于任意的i属于[1,n/2],i+n/2必定不属于[1,n/2]。那么我们在n/2次枚举中每次选取和比较大的那一段(等于的话随便取)。现在选出了n/2段,且长度为n/2,那么我们把它们长度往后扩展一位,这n/2段还是互不相等。
很直观的可以知道,n/2个开头中,必定存在一个开头,它之前一个开头代表的那一段没有被取,那么取那一段就行了。
由抽屉原理可得,必定有一串同时满足ab两个数组,于是枚举便可。
证明2:
n=2*k+1:
[i,i+k]与[i+k,i+2k]必定有一个或者两个满足题目条件。那么把所有的这样的对连边,便构成一个环(点n个)。满足条件的点必定大等于k+1个。
n=2*k:
末尾补零就变成n=2*k+1的情况。
#include<cstdio>
#include<algorithm>
using namespace std;
struct Node {
int a,b,i;
bool operator < (const Node &tmp) const {
if(a==tmp.a) return i<tmp.i;
return a>tmp.a;
}
}a[100005];
int main() {
///
int n;scanf("%d",&n);
///init
for(int i=1;i<=n;++i) a[i].i=i;
///read
for(int i=1;i<=n;++i) scanf("%d",&a[i].a);
for(int i=1;i<=n;++i) scanf("%d",&a[i].b);
///solve
sort(a+1,a+1+n);
printf("%d\n%d",n/2+1,a[1].i);
if(n%2==0) printf(" %d",a[n].i);
for(int i=2;i+1<=n;i+=2) {
printf(" %d",(a[i].b>a[i+1].b)?a[i].i:a[i+1].i);
}
puts("");
return 0;
}
#include<cstdio>
typedef long long ll;
const int N=200005;
int n;
int a[N],b[N];
void solve() {
///get s
ll sa=0,sb=0,nowa=0,nowb=0;
for(int i=0;i<n;++i) {
sa=sa+a[i];
sb=sb+b[i];
if(i<=n/2) {
nowa=nowa+a[i];
nowb=nowb+b[i];
}
}
///solve
int l=0,r=n/2;
while(r<2*n) {
if(nowa*2>sa&&nowb*2>sb) {
printf("%d\n",n/2+1);
for(int i=l;i<=r;++i) printf("%d ",i%n+1);
puts("");
return ;
}
r++;
nowa=nowa-a[l]+a[r];
nowb=nowb-b[l]+b[r];
l++;
}
}
int main() {
///
scanf("%d",&n);
///read
for(int i=0;i<n;++i) scanf("%d",a+i),a[i+n]=a[i];
for(int i=0;i<n;++i) scanf("%d",b+i),b[i+n]=b[i];
///solve
solve();
return 0;
}