codeforces 798D Mike and distribution

题意:给出数组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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值