acw周赛16t2 截断数组

自己的勉强ac代码
复杂度之前错算了,优化前的复杂度是O(n2)级别的,直接超时
虽然是有前缀和的思路,但并没有建前缀和数组,对于每一个第二次下标还是要遍历前面的下标
但通过优化和猜数据居然过了,有点…不知道该说什么
而且总感觉哪里还是不对劲,感觉集合更新数据还是写错了但我没有证据
最绝望的是ac交上去之后看了一眼表,比赛刚好结束一分钟…
谢谢,血压高起来了.jpg

#include<bits/stdc++.h>
using namespace std;

#define MAX 100005
#define ll long long

int n;
int a[MAX];

int main()
{
	int x=0,y=1;//截断处的最后一个下标
	bool flag=0;
	ll int xx,yy,zz;//三个数组的和
	ll max=0;
	int i,j;
	ll int ans=0;
	cin>>n;
	for(i=0;i<n;i++)
	{
		cin>>a[i];
		max+=a[i];
		if(a[i]!=0) flag=1;
	}
	if(n<3&&max%3!=0)//对于过少数据和无法整除的处理
	{
		cout<<0;
		return 0;
	}
	if(flag==0)//对全是0的处理
	{
		ans=0;
		for(i=1;i<n-1;i++) ans+=i;
		cout<<ans;
		return 0;
	}
	xx=a[0];//第一段集合的和
	yy=a[1];//第二段集合的和
	zz=max-xx-yy;//第三段集合的和
//现在看这一段属实多余,至少中间那段是真的多余
	for(x=0;x<n-2;x++)//遍历第一个间隔
	{
		if(x!=0)//对前两个集合的和重新计算,因为用的不是前缀和数组
		//(一个暑假过去直接忘了前缀和数组这回事了,光记得前缀和这个思路orz)
		{
			xx+=a[x];
			yy=a[x+1];
			zz=max-xx-yy;
		}
		if(xx!=max/3) continue;//优化,整除不了直接跳
		for(y=x+1;y<n-1;y++)//遍历第二个间隔
		{
			if(y!=x+1)//对后两个数组的重新计算
			{
				yy+=a[y];
				zz-=a[y];
			}
			if(xx==yy&&yy==zz) ans++;
		}
	}
	cout<<ans;
	
	return 0;
 } 

y总的代码和自己的注释,复杂度为O(n)
本来觉得遍历就是简单粗暴的做法,没想到还能玩出这种操作
数学思维和想象力还是差了太多
还是题做的不够多.jpg

#include <iostream>
#include <cstring>
#include <algorithm>
//我自己还是更喜欢万能头的
using namespace std;

typedef long long LL;

const int N = 100010;

int n;
int s[N];//前缀和数组

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &s[i]);
        s[i] += s[i - 1];//计算前缀和
    }

    if (s[n] % 3)//如果总和除不尽3,直接输出0结束
    {
        puts("0");
        return 0;
    }

    LL res = 0;//存放答案,答案数量可能很多
    for (int i = 3, cnt = 0; i <= n; i ++ )//从头开始遍历第二个间隔
    {
        if (s[i - 2] == s[n] / 3) cnt ++ ;//计算第一个间隔是否满足条件
        /*
        cnt用来保存当前可用的第一个间隔的位置。
        第一个间隔的开头是固定的(首项),
        最后一个间隔的末尾也是固定的(末项)。
        由此很容易算出三段的具体数值。
        如果在某个地方的第一个间隔出现了第一段数组满足1/3的情况,
        那么只需要判断后两段数组是否满足1/3即可。
        而事第一段满足条件的第一个间隔后的第二个间隔的位置是任意的,只要满足条件都可以。
        cnt就是当前的第二个间隔可用的所有满足条件的第一个间隔的数量,
        从前往后遍历的情况下先成立的第一个间隔适用于后面所有的第二个间隔。
        (但反之不成立,所以必须边进行边遍历)
        在第二个间隔能将第三段集合满足1/3时,和第一段集合联立则第二段一定也是1/3
        即满足条件
        */
        if (s[n] - s[i - 1] == s[n] / 3) res += cnt;//判断第三段间隔是否满足条件
    }
	/*
	说实话这个思路还是蛮绕的
	至少我第一次看视频根本就摸不着头脑
	直接扔了视频看代码反而才看出来名堂
	这个遍历就是在遍历所有第二段间隔
	同时遍历所有的第一段间隔
	有三种情况:
	1.两段间隔都做不到将前后两段集合满足条件,那么什么都不发生,接着遍历
	2.前一段间隔做到了而后一段没做到,那么存起来可能的第一段间隔,可以给后面所有的第二段间隔使用
	因为前满足,后满足,中间就一定满足
	3.后一段满足前一段不满足,只将前面所有存起来的第一个间隔数量加入答案中
	4.前后两段间隔都能满足切割条件,先存起来第一段间隔,再算答案。
	同一次遍历的第一段间隔都是第二段间隔能用上的
	*/
    printf("%lld\n", res);
    return 0;
}

//作者:yxc
//链接:https://www.acwing.com/activity/content/code/content/1770756/
//来源:AcWing
//著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值