洛谷P3131 Subsequences Summing to Sevens S
阿新 • • 發佈:2021-11-13
洛谷P3131 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 (1 \leq N \leq 50,000)\)如果採用直接列舉端點\(O(n^2)\)的時間複雜度基本是要掛的。下面針對暴力方法一步一步進行優化。
原始的暴力程式碼
int len = -1;
for (int i = 1; i <= n; i ++)
for (int j = i + 1; j <=n; j ++)
if ((s[j]-s[i-1]) % 7 == 0) len = max(len, s[j]-s[i-1])
之後仔細觀察發現,由於所求的是區間長度的最大值,所以內層迴圈可以從\(n\)開始想前列舉,遇到可以除盡7的情況\(break\)即可。但是這麼做只能從60分變到70分。
70分暴力
int len = -1; for (int i = 1; i <= n; i ++) for (int j = n; j > i; j --) if ((s[j]-s[i-1]) % 7 == 0) { len = max(len, j-i+1); break; }
進行到目前為止,已經意識到必須改變方法了。這裡要用到一個關於\(mod\)運算的小定理
如果\((a - b) \mod k == 0\) 則\(a\mod k = b \mod k\)
通俗的來講就是兩個數相減可以除盡一個數,那這兩個數除這個數的餘數一定相等
那我們只需要計算所有字首和對除7的餘數,將餘數相同的字首和下標,從小到大的放在vector
陣列中,由於是從小到大,所以只需要將最後一個下標減去第一個下標即可。
需要注意的幾個事情
- 最後結果直接就是下標相減不要加一,因為由上面暴力解法可知,下標相減就是\(j-i+1\)
- 一定要注意加上
h[0].push_back(0)
vector
陣列並不是每一中餘數都相等,所以有的餘數對應的陣列size
是0,一定要注意,否則會造成陣列越界,一定要在看見陣列中出現a[i-1]
這種情況時考慮是不是會造成陣列越界
附上完整AC
程式碼
#include<bits/stdc++.h>
using namespace std;
const int N = 50010;
int a[N], n;
long long s[N];
vector<int> h[20];
int main ()
{
cin >> n;
for (int i = 1; i <= n; i ++) scanf("%d", &a[i]);
h[0].push_back(0);
for (int i = 1; i <= n; i ++) {
s[i] = s[i-1] + a[i];
h[s[i] % 7].push_back(i);
}
int len = -1;
for (int i = 0; i < 7; i ++) {
int l = h[i].size();
if (!l) continue;
len = max(h[i][l-1] - h[i][0], len);
}
if (len == -1) cout << 0;
else cout << len;
return 0;
}