P3131 [USACO16JAN]Subsequences Summing to Sevens S
给你n个数,分别是a[1],a[2],…,a[n]。求一个最长的区间[x,y],使得区间中的数(a[x],a[x+1],a[x+2],…,a[y-1],a[y])的和能被7整除。输出区间长度。若没有符合要求的区间,输出0。
输入格式
第一行输入一个 N N N ( 1 ≤ N ≤ 50 , 000 1 \leq N \leq 50,000 1≤N≤50,000). 接下来 N N N
行输入 N N N 个整数 (取值范围为
0 … 1 , 000 , 000 0 \ldots 1,000,000 0…1,000,000).
输出格式
输出区间和能被7整除的最大区间长度。若没有符合要求的区间,输出0。
样例 #1
样例输入 #1
7
3
5
1
6
2
14
10
样例输出 #1
5
提示
在这个样例中, 5+1+6+2+14 = 28.
【问题分析】
有区间和问题,采用前缀和来进行解题。
计算出所有的前缀和后,使用
s
[
i
]
−
s
[
j
]
s[i]-s[j]
s[i]−s[j]就可以求出区间和了,但是这样做的时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)。属于暴利求解(枚举法)。
像这样
#include<bits/stdc++.h>
using namespace std;
int a[50005],s[50005];
int first[7],last[7];
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=s[i-1]+a[i];
}
//假设i是右区间,j是左区间
int mx=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=i;j++){
if((s[i]-s[j])%7==0){
mx=max(mx,i-j);
}
}
}
cout<<mx;
return 0;
}
出现超时问题,只能通过60分。
此时我们需要修改策略。
在做前缀和时对7取模,同时设置两个存储余数的数组:
1.一个存储余数第一次出现的位置,另一个存储余数最后一次出现的位置。
2.如果两个余数相同,那么其区间和必定被7整除,这样事件复杂度就会下降,超时问题得以解决。
像这样
#include<bits/stdc++.h>
using namespace std;
int a[50005],s[50005];
int first[7],last[7];
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=(s[i-1]+a[i])%7;
}
int mx=0;
for(int i=n;i>=1;i--){
first[s[i]]=i;//第一次出现s[i]余数的位置是i
}
first[0]=0;//0的第一次出现位置设为0
for(int i=1;i<+n;i++){
last[s[i]]=i;//最后一次出现s[i]余数的位置是i
}
for(int i=0;i<=6;i++){
mx=max(last[i]-first[i],mx);
}
cout<<mx;
return 0;
}