题161.洛谷P3131 前缀和与差分-Subsequences Summing to Sevens S
一、题目
二、题解
看到这题,开局便想着我应该构建个前缀和数组,然后去用两个循环去求出满足被7整除的区间前缀和,但是这又毫无意外的超时了。以下是那个超时代码:
#include <bits/stdc++.h>
using namespace std;
int N;
int a[50010];
int sum[50010];
int main()
{
cin>>N;
for(int i=1;i<=N;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
int maxlen=0;
for(int l=0;l<N;l++)
{
for(int r=l;r<=N;r++)
{
int val=sum[r]-sum[l];
if(val%7==0)
{
maxlen=max(maxlen,r-l);
}
}
}
cout<<maxlen<<endl;
}
于是我们想着如何将循环再去掉一层这样去优化它。有个定理是这样的,(a-b)%c=0=>a%c=b%c,正好这里我们用前缀和数组去求区间和的方式便是sum[r]-sum[l],所以要使得(sum[r]-sum[l])%7=0对应的区间长度最大,即得到sum[l]%7=sum[r]%7,r-l值最大就好。所以关键是记录下相同余数开始出现的位置与最后出现的位置,找到他们最大差值出来就好。AC代码如下:
#include <bits/stdc++.h>
using namespace std;
int N;
int a[50010];
int sum[50010];
int pre[7],post[7];
int main()
{
cin>>N;
for(int i=1;i<=N;i++)
{
scanf("%d",&a[i]);
sum[i]=(sum[i-1]+a[i])%7;//因为只要知道余数的情况,所以直接存前缀和与7的余数就好
}
for(int i=N;i>=0;i--)//倒着扫,找正序第一次出现的各个余数的位置
{
pre[sum[i]]=i;
}
for(int i=0;i<=N;i++)//正着扫,找正序最后一次出现的各个余数的位置
{
post[sum[i]]=i;
}
//切记上面两个循环都得扫到i=0,因为可能出现sum只有一个能被7整除的情况,这时候它的长度显然i-0,而不是不扫到i=0出现的i-i=0
int maxlen=0;
for(int i=0;i<7;i++)
{
maxlen=max(maxlen,post[i]-pre[i]);
}
cout<<maxlen<<endl;
}