1
能量棒 energy
1.1
题目描述
能量棒是一个神奇的东东。它是由若干个能量点组成的。能量点有 2 种类型,这两种类型分
别能释放出 1 或 2 的能量。
现在,给你一个长为 n 的能量棒,有 q 个询问,每次给定一个 k ,询问是否能从这个能量
棒中找到一个连续子段,使得这个子段释放的能量恰好为 k 。
1.2
输入格式
第一行两个数 n,m ,意义见题。
接下来一个长为 n 的 1/2 字符串,描述这个能量棒。
接下来 m 行,每行一个数 k ,表示一个询问。
1.3
输出格式
对于每个询问,如果能找到一个连续子段,输出一行两个数,表示这个子段的起始点和终止
点,如果有多组解,任意一组即可。否则输出 0 0 。
1.4
样例输入
5 3
12121
5
1
7
1.5
样例输出
2 4
3 3
1 5
1.6
数据范围
对于 30% 的数据:n ≤ 103,m ≤ 104
对于 100% 的数据:n ≤ 106,m ≤ 106
这道题(对于 LYP 的题我已经有心理阴影了。。。。)考场上先写了 O(n * m) 的暴力,然后又想到了一个有点像正解的奇葩方法,不过写完一拍一错千里,于是马上交了暴力。
先说一下我的奇葩方法:首先对于一段子段和,显然可以通过随意删去首或尾来凑出与之同奇偶性的子段和。然后因为当时觉得对于偶数的限制比较小(毕竟全部是 2 是出不了奇数的,而全部是 1 可以出偶数),所以我就求了一个最大的奇数子段和,然后碰到奇数就到其中去找,而偶数就一顿乱找。
再来讲正解:[可以通过随意删去首或尾来凑出与之同奇偶性的子段和] 这条性质我已经说过了。那么,可以通过找到一个最大的奇数子段和和偶数子段和,然后在其中查找。然而又有这样一个性质:在最大的奇数子段和和最大的偶数子段和中,必然有其一为总和;而在其中寻找与之异奇偶的子段和,去首去尾则必有其一为偶数,对于奇偶性无影响,也就是说只需要删去首或尾之一即可。于是从头到尾扫两遍即可,然后预处理出所有答案,对于每一个询问 O(1) 输出即可,复杂度 O(n + m)。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define swap(a, b, t) ({t _ = (a); (a) = (b); (b) = _;})
#define max(a, b) ({int _ = (a), __ = (b); _ > __ ? _ : __;})
#define min(a, b) ({int _ = (a), __ = (b); _ < __ ? _ : __;})
#define maxn 2000005
#define sum(i, j) (s[j] - s[(i) - 1])
int n, m, s1, s2;
int a[maxn], s[maxn], ll[maxn], rr[maxn];
void getit(int l, int r, int k)
{
for (; l <= r; k -= 2)
{
ll[k] = l, rr[k] = r;
if (a[l] == 2) ++ l;
else if (a[r] == 2) -- r;
else ++ l, -- r;
}
}
void work()
{
for (int k; m --; )
scanf("%d", & k), printf("%d %d\n", ll[k], rr[k]);
}
void init()
{
scanf("%d%d\n", & n, & m);
for (int i = 1; i <= n; ++ i)
{
char ch = getchar();
s[i] = s[i - 1] + (a[i] = ch - '0');
}
int l, r; getit(1, n, sum(1, n));
for (l = 1; l <= n; ++ l) if (a[l] == 1) break;
for (r = n; r >= 1; -- r) if (a[r] == 1) break;
sum(l + 1, n) > sum(1, r - 1) ? getit(l + 1, n, sum(l + 1, n)) : getit(1, r - 1, sum(1, r - 1));
}
int main()
{
freopen("energy.in", "r", stdin);
freopen("energy.out", "w", stdout);
init();
work();
return 0;
}