zoukankan      html  css  js  c++  java
  • AtCoder Regular Contest 100 (ARC100) D

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

    题目传送门 - ARC100D

    题意

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

      $nleq 2 imes 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;
    }
    

      

  • 相关阅读:
    MSSQLSERVER数据库 变量和批处理
    MSSQLSERVER数据库 事务
    MSSQLSERVER数据库 使用C#来操作事务
    MSSQLSERVER数据库 视图
    Delphi中DBGrid行列颜色设置
    C#中调用SQL存储过程(带输入输出参数的例子)
    用Visual C#调用Windows API函数
    c#日期时间的操作的函数总结[转]
    C# 序列化与反序列化
    利用C#事务处理对数据库进行多重操作
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/9251420.html
Copyright © 2011-2022 走看看