【算法竞赛】相邻差值相等问题的前缀和解法
这个问题名字是我自己口胡的。
其问题的大致模板是:题意可以转化或特殊设计前缀和变成求相邻两个位置之间连续“差值相等”的最长长度。
解法是根据前缀和的公式:
例1.“非常男女”计划
我们可以对前缀和进行特殊设计,设男生为
那么,我们要找的区间
我们要记录最长的区间,实际上就是记录
用桶记录一下即可,时间复杂度
注意:为了防止统计时出现负数导致RE,我们可以把每一次放入桶的sum[i]+n,这样就可以把负数转成正数。还有一些写代码时的细节。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
int n;
int a[maxn], sum[maxn];
int tl[2 * maxn], tr[2 * maxn]; // 记录左边、右边的桶(别忘了开二倍空间,因为统计的是sum[i]+n)
void solve()
{
cin >> n;
for(int i = 1; i <= n; i ++){
cin >> a[i];
if(a[i] == 0) sum[i] = sum[i - 1] - 1;
if(a[i] == 1) sum[i] = sum[i - 1] + 1; // 前缀和
if(tl[sum[i] + n] == 0){ // 记录第一次出现
tl[sum[i] + n] = i;
}
tr[sum[i] + n] = i; // 因为i一定会越来越大,所以最后一次出现直接顺着记下来就行
}
tl[n] = 0; // 0第一次出现是在位置0(表示成0+n)
int ans = 0;
for(int i = 0; i <= 2 * n; i ++){
ans = max(ans, tr[i] - tl[i]); // 此时看作区间是[ tl[i]+1,tr[i] ]
}
cout << ans << '\n';
}
signed main()
{
ios :: sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
solve();
return 0;
}
例2.Gold Balanced Lineup G / 暗杀
可以看作 P1114“非常男女”计划 的升级版。
读懂题意后发现,我们要找到一段最长的序列,使得其相邻两个位置的差相同。
可以联想到前缀和,我们只要统计前面所有数的前缀和,然后看 当前前缀和-上一个前缀和的值 出现次数最多的长度即可。
(如果对于一组
因为这个差值一定是全部相等的,我们可以用 map 记录下当前的差值状态第一次出现的位置,然后,之后每出现相同的差值状态,就用当前位置减去map记录的位置,得出中间的长度。
别忘了开 long long!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
int n, m;
map<vector<ll>, int> vis; // 创新地使用vector+map来做桶
ll s[maxn][40];
void solve()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++){
int a; cin >> a;
for(int j = 0; j < m; j ++){ // 提取出二进制的位数
if(a >> j & 1) s[i][j] = s[i - 1][j] + 1;
else s[i][j] = s[i - 1][j];
}
}
vector <ll> now;
for(int i = 1; i <= m - 1; i ++){ // 因为有可能1~n全相等,所以要加入全0的状态
// 因为长度为k的数组只有k-1个差分值,所以这里放入k-1个数字即可
now.push_back(0);
}
vis[now] = 0; // 当前时间是0
int ans = 0;
for(int i = 1; i <= n; i ++){
now.clear(); // 先清空
for(int j = 0; j < m - 1; j ++){
now.push_back(s[i][j + 1] - s[i][j]); // 相邻两位的前缀和之差
}
if(vis.count(now)){ // 之前出现过,就统计答案
ans = max(ans, i - vis[now]);
}
else{ // 否则记录第一次出现的位置
vis[now] = i;
}
}
cout << ans << '\n';
}
signed main()
{
ios :: sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
solve();
return 0;
}