P3131 [USACO16JAN] Subsequences Summing to Sevens S

题目链接:

直接暴力的话时间复杂度为 \(O(n^2)\),显然会 \(\sf TLE\)

#include <bits/stdc++.h>

using namespace std;

const int N = 5e4 + 10;

int a[N], S[N];

int main()
{
	ios::sync_with_stdio(false), cin.tie(nullptr);
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		S[i] = S[i - 1] + a[i];
	}
	bool flag = false;
	int ans = 0;
	for (int len = n; len >= 1; len--) {
		for (int i = 1; i + len - 1 <= n; i++) {
			int j = i + len - 1;
			if ((S[j] - S[i-1]) % 7 == 0) {
				ans = max(ans, len);
				flag = true;
			}
		}
	}
	if (!flag) cout << 0;
	else cout << ans;
	return 0;
}

考虑到性质:若要让 $ (a[j]-a[i-1]) \bmod 7$ 为 \(0\),只需让 $ a[j]$ 和 \(a[i-1]\)\(7\) 的余数相等即可。

所以只要求出相同余数第一次出现和最后一次出现之间的距离即可。(直接相减,即为\((j-i+1)\)

从前往后依次遍历可以找到某数最后一次出现的下标,同样从后往前依次遍历可以找到某数第一次出现的下标。

#include <bits/stdc++.h>

const int N = 5e4 + 10;

int a[N], first[N], last[N];

int main()
{
	std::ios::sync_with_stdio(false), std::cin.tie(nullptr);
	int n;
	std::cin >> n;
	for (int i = 1; i <= n; i++) {
		std::cin >> a[i];
		a[i] = (a[i - 1] + a[i]) % 7;//模 7 意义下的前缀和,即为前缀和模 7 的余数
	}
	
	for (int i = n; i >= 1; i--) {
		first[a[i]] = i;//first[x] 表示模 7 余数为 x 的数第一次出现的下标
	}
	first[0] = 0;//极为重要!
	for (int i = 1; i <= n; i++) {
		last[a[i]] = i;//last[x] 表示模 7 余数为 x 的数最后一次出现的下标
	}
	int ans = 0;
	for (int i = 0; i < 7; i++) {
		ans = std::max(ans, last[i] - first[i]);
	}
	std::cout << ans;
	return 0;
}

如不加 \(\rm first[0]=0\) 的话,当从头一直加到下标为 \(i\) 的前缀和是 \(7\) 的倍数时,模 \(7\) 意义下的前缀和 \(a[i]=0\),这时必须把 \(\rm first[0]\) 置为 \(0\) 才说明把 \(1 \sim i\) 的这些数都选上了。

posted @ 2024-04-17 19:47  胖柚の工作室  阅读(21)  评论(0)    收藏  举报