首页 > 其他分享 >P3514

P3514

时间:2023-08-15 12:28:44浏览次数:38  
标签:算入 端点 int bool 答案 ans P3514

题目传送门

这是一个稍微复杂一点且较容易想到的做法。

考虑 特殊到一般,我们固定一个右端点 \(l\),枚举左端点 \(r\),将 \([l,r]\) 算入答案。这样显然会漏掉一些答案,发现必然是进行 \(+2\) 时跳过了一个数,考虑如何得到这个数。

发现如果 \(a_l=1\),我们将 \(l\leftarrow l+1\),则枚举左端点 \(r\) 得到的数恰好对于上一次计算的数 \(-1\),可以补上。那么我们找到第一个出现 \(1\) 的位置也就是现在我们得到了 \([1,s_{l,n}]\) 的答案,其中 \(s_{l,r}\) 表示 \(\sum\limits_{i=l}^{r}a_i\) 。

考虑还要将 \([1,l-1]\) 的 \(2\) 算入答案,这个我们在以 \(l\) 为左端点时对计算的和 \(s_{l,r}\) 记录数组 \(b_{s_{l,r}}\) \(l\) 前面可以拓展多少个 \(2\),对于每个 \(i\) 存在答案且 \(b_i>0\),都可以计算出 \(i+2\) 的答案,从小到大递推即可,具体见代码。

bool START;

const int N = 2e6 + 5;

int n, q, a[N], b[N];
pr ans[N];
char s[N];

bool END;

void cl(int l, bool fl) {
  if (fl) b[0] = l - 1;
  for (int i = l, sm = 0; i <= n; ++i) {
    sm += a[i], ans[sm] = mk(l, i);
    if (fl) b[sm] = l - 1;
  }
}

void cl2() {
  for (int i = 0; i <= 2 * n; ++i)
    if (ans[i] != mk(0, 0) && b[i] > 0 && (ans[i + 2] == mk(0, 0) || b[i + 2] < b[i] - 1))
      b[i + 2] = b[i] - 1, ans[i + 2] = ans[i], ans[i + 2].fi--;
}

signed main() {
  IN(n), IN(q), scanf(" %s", s + 1); for (int i = 1; i <= n; ++i) a[i] = (s[i] == 'T') ? 2 : 1;
  int p = n + 1;
  for (int i = 1; i <= n; ++i) if (a[i] == 1) {p = i; break;}
  cl(p + 1, 0), cl(p, 1),  ans[0] = mk(p, p - 1), cl2();
  while (q--) {
    int x; IN(x);
    if (ans[x] == mk(0, 0)) puts("NIE");
    else cout << ans[x].fi << ' ' << ans[x].se << endl;
  }
  return 0;
}

标签:算入,端点,int,bool,答案,ans,P3514
From: https://www.cnblogs.com/Tagaki-san/p/17631005.html

相关文章

  • P3514题解
    给一个只有1和2的序列,每次询问有没有一个子串的和为xSPJ对于格式的要求较为严格。对于每个询问后,应紧跟一个换行符。在最后一次输出你的答案以及一个换行符后不应有任何输......
  • P3514 [POI2011]LIZ-Lollipop
    给定长度为\(n\)的序列,里面的元素为1或2,求是否有一种方案,取出连续的一段,使得到的元素和等于给定的值,若可以则输出一种方案。多组询问,\(n,q\leq10^6\)。感觉有点水,典......