[构造] 能量棒 energy

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;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值