题目:
洛谷 - P2697 宝石串
洛谷 - P1114 “非常男女”计划
题目大意:
给出一个序列
a
(
∣
a
∣
≤
1
0
6
)
a\ (|a| ≤ 10^6)
a (∣a∣≤106) ,其种只可能出现两种元素。如果某个子序列中两种元素的个数相同,就可以说这个序列是平衡的。
(链接在上面了,不懂可以自己看原题。
∣
a
∣
|a|
∣a∣ 代表
a
a
a 的长度)
要实现的问题:如何判定一个序列是否平衡
(假如现在要判断序列
k
k
k 是否平衡)
首先,要让两种元素个数相同,那么
∣
k
∣
|k|
∣k∣ 就必须是偶数,否则就不可能平衡。
第二,如果
k
k
k 中元素 A 的个数等于
1
2
∣
k
∣
{1 \over 2}|k|
21∣k∣ 那么它就是平衡的,否则不平衡。在这一步可以使用前缀和优化,假设 A 是
1
1
1 ,B 是
0
0
0 ,那么这里的区间就等于 A 的个数。
bool check(int left, int right) // 左闭右闭区间
{
if ((right - left + 1) % 2 == 1)
{
return false;
}
if (sum[right] - sum[left - 1] == (right - left + 1) / 2)
{
return true;
}
return false;
}
这就是这道题的核心部分。可是 ∣ a ∣ |a| ∣a∣ 都到 1 0 6 10^6 106 了,循环嵌套肯定得超时啊
但事实证明,在加上了亿点点优化以后,在这两道题里确实能过。
所以就贴代码吧(枚举前面不做解释)
“正解”(宝石串)
#include <bits/stdc++.h>
using namespace std;
int n;
int a[100010], sum[100010];
int maxl = 0;
int main()
{
string s;
cin>>s;
n=s.size();
s=' '+s;
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1];
if(s[i]=='G')
{
sum[i]++;
}
}
for (int i = 1; i <= n;i++)
{
int j;
if(maxl == 0)
{
j = i + 1;
}
else
{
j = i + maxl - 1;
}
for (; j <= n;j+=2)
{
int l = j-i+1;
if(sum[j]-sum[i-1] == l/2)
{
maxl = max(maxl,l);
}
}
}
cout << maxl;
}
// 因为判断很容易所以这里就没写 check 函数
你应该注意到了,上面的正解是带引号的。它确实不是真正的正解,只是数据太水了而已(之前在 Turing 里做过一个改版,会 WA 掉两个点)。
那么怎么做呢?
前面说过可以把元素当作
0
0
0 和
1
1
1 来看。但还有一种想法:可以看做
1
1
1 和
−
1
-1
−1 ,这样只要某个区间的和是
0
0
0 ,那么它就平衡了。
而区间和等于 sum[right] - sum[left - 1]
。如果某区间等于
0
0
0 ,也就意味着sum[right] == sum[left - 1]
。反之,只要满足这个条件,该区间也就平衡。
那么问题就简化为了
(在前缀和中)找一个相同,且距离最远的数,然后求出差值。
这下就简单了。往后遍历,用一个 map 记录某个(前缀和中的)值第一次出现的下标;如果某个值第二次出现就更新答案。
注:map中一开始要加入一个元素 0 ,并将 map[0] 设为 0。因为要枚举的是一个左开右闭的区间。如果某个 sum[k] == 0
,也就是它前面的就是一个平衡序列,但是前面没有记录过 0 ,导致答案没有更新。
能想到这一步应该就能很快写出来了:
正解(真的)
#include <iostream>
#include <map>
#include <string>
using namespace std;
string s;
map<int, int> mp;
int sum[1000010], ans = 0;
int main()
{
mp[0] = 0;
cin >> s;
for (int i = 1; i <= s.size(); i++)
{
sum[i] = sum[i - 1];
if (s[i - 1] == 'G')
{
sum[i]++;
}
else
{
sum[i]--;
}
}
for (int i = 1; i <= s.size(); i++)
{
auto it = mp.find(sum[i]);
if (it != mp.end())
{
ans = max(i - it->second, ans);
}
else
{
mp[sum[i]] = i;
}
}
cout << ans << '\n';
return 0;
}
蒟蒻写代码不容易,点个赞在走吧