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; }