//题意:把数列a分成相等的三部分S(1,i-1)=S(i,j)=S(j+1,n) 求断点(i,j)的选法个数
//3*x=sum,假设当前找到pre[i-1]=x 则只要找到下标j>=i&&pre[j]=2x 的个数即可,剩下(j+1,s)=3x-2x=x;
//保存pre[j]=2x的下标 当pre[i-1]==x时二分出第一个>i-1的j即可
O(nlogn)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=5e5+20;
ll a[N],pre[N];
vector<int> pos;
int main()
{
int n;
while(cin>>n)
{
ll ans=0,sum=0;
memset(pre,0,sizeof(pre));
for(int i=1;i<=n;i++)
{
scanf("%I64d",&a[i]);
pre[i]=pre[i-1]+a[i];
sum+=a[i];
}
ll x;
if(sum%3)
{
puts("0");
continue;
}
x=sum/3;
for(int i=1;i<n;i++)//第二段端点j要求i<=j<n
{
if(pre[i]==x*2)
{
pos.push_back(i);
}
}
for(int i=2;i<n;i++)
{
if(pre[i-1]==x)
{
//abs(a[i])<=1e9 pre[j]=2x,pos中下标j是有序的
int cnt=pos.end()-upper_bound(pos.begin(),pos.end(),i-1);//找到第一个大于i的下标
ans+=cnt;
}
}
cout<<ans<<endl;
pos.clear();
}
return 0;
}标程的做法 O(n)
#include <iostream>
#include <math.h>
using namespace std;
int a[1000010];
long long cnt[1000010];
int main()
{
ios::sync_with_stdio(0);
int n;
cin >> n;
long long s = 0;
for(int i = 0 ; i < n ; ++i)
{
cin >> a[i];
s += a[i];
}
if (s % 3 != 0)
cout << "0\n";
else {
s /= 3;
long long ss = 0;
for(int i = n-1; i >= 0 ; --i)
{
ss += a[i];
if (ss == s)
cnt[i] = 1;
}
for(int i = n-2 ; i >= 0 ; --i)
cnt[i] += cnt[i+1];
long long ans = 0;
ss = 0;
for(int i = 0 ; i+2 < n ; ++i) {
ss += a[i];
if (ss == s)
ans += cnt[i+2];
}
cout << ans << "\n";
}
return 0;
}

本文介绍了一种将数列均匀分为三个子序列的算法实现,提供了两种不同复杂度的解决方案,分别为O(nlogn)和O(n)。该算法通过预处理数列之和并寻找特定位置的下标来计算分割数列的方法数量。

1606

被折叠的 条评论
为什么被折叠?



