AtCoder Regular Contest 100 (ARC100) D - Equal Cut 二分

原文链接https://www.cnblogs.com/zhouzhendong/p/9251420.html

题目传送门 - ARC100D

题意

  给你一个长度为 $n$ 的数列,请切 $3$ 刀,形成 $4$ 个连续非空子序列,问这 $4$ 个非空子序列的各自的元素和 的极差为多少。

  $n\leq 2\times 10 ^5$

题解

  如果切一刀,那么问题就很简单,尽量选中间的就可以了。

  可以二分一下在 $O(\log n)$ 的复杂度内解决。

  于是本题可以先枚举一下中间那条线,然后对于两边转化成刚才的子问题。

  然而!

  关键是:我居然没有想到,大概是脑抽了吧。

  然而!

  我通过乱搞过掉了!!

  本题第一次让我感受到了乱搞的强大!!

  UPD:突然发现正解可以通过指针扫一遍实现 $O(n)$ 。

代码

  正解我没写过。这里放乱搞,仅供观看,别喷谢谢。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=200005;
int n;
LL a[N],sum[N],v;
int x[5]={0};
LL calc(int i){
	return sum[x[i]]-sum[x[i-1]];
}
LL llabs(LL x){
	return x<0?-x:x;
}
bool cmp(int a,int b){
	return calc(a)<calc(b);
}
bool cmp2(int a,int b){
	return calc(a)>calc(b);
}
void move(int id,int d){
	if (d==1){
		if (x[id+1]-x[id]>1)
			x[id]++;
	}
	else {
		if (x[id]-x[id-1]>1)
			x[id]--;
	}
}
LL trys(){
	LL ans=1e17;
	int y[5];
	for (int i=0;i<5;i++)
		y[i]=x[i];
	for (int i=1;i<=20;i++){
		int id[5]={0,1,2,3,4};
		sort(id+1,id+5,cmp);
		ans=min(ans,llabs(calc(id[1])-calc(id[4])));
		int r=rand()%6;
		if (r==0){
			LL val=calc(1);
			if (val>v)
				move(1,-1);
			else
				move(1,1);
		}
		if (r==1){
			LL val=calc(4);
			if (val>v)
				move(3,1);
			else
				move(3,-1);
		}
		if (r>1){
			r-=2;
			int k=r&1?2:3;
			r/=2;
			LL val=calc(k);
			if (r){
				if (val>v)
					move(k-1,1);
				else
					move(k-1,-1);
			}
			else {
				if (val>v)
					move(k,-1);
				else
					move(k,1);
			}
		}
	}
	for (int i=0;i<5;i++)
		x[i]=y[i];
	return ans;
}
LL solve(){
	for (int i=1;i<=n;i++)
		sum[i]=sum[i-1]+a[i];
	v=sum[n]/4;
	for (int i=1;i<=3;i++){
		x[i]=x[i-1]+1;
		while (x[i]<n+i-4&&sum[x[i]]-sum[x[i-1]]<=v)
			x[i]++;
	}
	x[4]=n;
	LL ans=1e17;
	for (int xxxx=1;xxxx<=n*4;xxxx++){
		ans=min(ans,trys());
		int id[5]={0,1,2,3,4};
		sort(id+1,id+5,cmp);
		ans=min(ans,llabs(calc(id[1])-calc(id[4])));
		int j=1;
		while (j<=4&&(id[j]==1||x[id[j]-1]-x[id[j]-2]==1))
			j++;
		if (j>4)
			break;
		x[id[j]-1]--;
	}
	return ans;
}
LL solve2(){
	for (int i=1;i<=n;i++)
		sum[i]=sum[i-1]+a[i];
	v=sum[n]/4;
	for (int i=1;i<=3;i++){
		x[i]=x[i-1]+1;
		while (x[i]<n+i-4&&sum[x[i+1]]-sum[x[i]]<=v)
			x[i]++;
	}
	x[4]=n;
	LL ans=1e17;
	for (int xxxx=1;xxxx<=n*4;xxxx++){
		int id[5]={0,1,2,3,4};
		sort(id+1,id+5,cmp2);
		ans=min(ans,llabs(calc(id[1])-calc(id[4])));
		int j=1;
		while (j<=4&&(id[j]==1||x[id[j]-1]-x[id[j]-2]==1))
			j++;
		if (j>4)
			break;
		x[id[j]-1]--;
	}
	return ans;
}
int main(){
	srand(19260817);
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	LL ans=1e17;
	ans=min(ans,solve());
	for (int i=1;i<=n/2;i++)
		swap(a[i],a[n-i+1]);
	ans=min(ans,solve());
	ans=min(ans,solve2());
	for (int i=1;i<=n/2;i++)
		swap(a[i],a[n-i+1]);
	ans=min(ans,solve2());
	printf("%lld",ans);
	return 0;
}

  

posted @ 2018-07-01 21:23  zzd233  阅读(340)  评论(0编辑  收藏  举报