把一个数列分成3个和相等的部分,有多少个分法。首先判断是否能被3整除,如果不被3整除就没得玩了。
思路1:
每部分的和是pre,先找等于pre的位置,然后是等于2pre的位置(位置<n),然后对第一个位置遍历,找第二个中位置大于第一种位置的个数,累加得答案,结果超时……
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
long long a[500005];
int dp1[500005], dp2[500005];
int main()
{
int i, j, m, n, temp1, temp2;
long long ans, pre, temp;
cin >> n; ans = 0;
for (i = 1; i <= n; i++)
{
cin >> a[i];
ans += a[i];
}
if (ans % 3 != 0)
cout << "0" << endl;
else
{
temp1 = temp2 = 1;
pre = ans / 3;
ans = 0;
for (i = 1; i <= n; i++)
{
ans = ans + a[i];
if (ans == pre&&i <= n - 2)
dp1[temp1++] = i;
if (ans == 2 * pre&&i <= n - 1)
dp2[temp2++] = i;
}
temp1--; temp2--;
temp = 0;
for (i = 1; i <= temp1; i++)
{
for (j = 1; j <= temp2; j++)
{
if (dp2[j] > dp1[i])
{
temp = temp + temp2 - j + 1;
break;
}
}
}
cout << temp << endl;
}
return 0;
}
思路2:
如果位置i符合和为pre,那么就是从i+2之后符合pre的第三组值的个数,只要第三组满足pre,第一组也满足pre,那么第二组一定是确定的而且和为pre。类似于动规了。从后向前找,这个从这个位置开始,符合和为pre的第三组的个数。
if(a[i]==pre)dp[i]=dp[i+1]+1;
else dp[i]=dp[i+1];
然后再从前向后找和为pre的第一组数,ans+=dp[i+2];至少留一个数给第二组。AC了。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
long long a[500005];
int dp[500005];
int main()
{
int i, j, m, n, temp1, temp2;
long long ans, pre, temp;
cin >> n; ans = 0;
for (i = 1; i <= n; i++)
{
scanf("%I64d", &a[i]);
ans += a[i];
}
if (ans % 3 != 0)
cout << "0" << endl;
else
{
temp1 = temp2 = 1;
pre = ans / 3;
ans = a[n];
if (a[n] == pre)dp[n] = 1;
else dp[n] = 0;
for (i = n - 1; i > 2; i--)
{
ans += a[i];
if (ans == pre)
dp[i] = dp[i + 1] + 1;
else
dp[i] = dp[i + 1];
}
ans = 0; temp = 0;
for (i = 1; i <= n - 2; i++)
{
ans = ans + a[i];
if (ans == pre)
temp = temp + dp[i + 2];
}
cout << temp << "\n";
}
return 0;
}
思路3:
在提交状态里,看见一个居然只有47ms的。
#include<bits/stdc++.h>
#define pf printf
#define sf scanf
#define ll long long
using namespace std;
template<class T>
inline bool fs(T &x)
{
int c=getchar();
int sgn=1;
while(~c&&c<'0'||c>'9')
{
if(c=='-')sgn=-1;
c=getchar();
}
for(x=0; ~c&&'0'<=c&&c<='9'; c=getchar())
x=x*10+c-'0';
x*=sgn;
return ~c;
}
int ara[500009],n;
ll x,y,a,b,c;
int main()
{
int i,j;
fs(n);
a=b=0;
x=y=0;
for(i=0; i<n; i++)
{
fs(ara[i]);
x+=ara[i];
}
for(i=0; i<n-1; i++)
{
y+=ara[i];
if(2*x==3*y)
b+=a;
if(3*y==x)
a++;
}
pf("%lld\n",b);
return 0;
}
其代码神奇之处在于一次遍历搞定两次遍历的活。如果找到了第一组满足pre,那么a++,如果找到了第二组也符合的,那么结果加上a。最后输出结果。这是另一种遍历方式。
然而他没有对3整除进行剪枝,加入这个判断后可以达到31ms,简直神奇。