1. 程式人生 > 其它 >洛谷P3131 Subsequences Summing to Sevens S

洛谷P3131 Subsequences Summing to Sevens S

洛谷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陣列中,由於是從小到大,所以只需要將最後一個下標減去第一個下標即可。

需要注意的幾個事情

  1. 最後結果直接就是下標相減不要加一,因為由上面暴力解法可知,下標相減就是\(j-i+1\)
  2. 一定要注意加上h[0].push_back(0)
    如果沒有這句會少一種情況。
  3. 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;
}