首页 > 其他分享 >P9576 「TAOI-2」Ciallo~(∠・ω< )⌒★

P9576 「TAOI-2」Ciallo~(∠・ω< )⌒★

时间:2024-01-20 21:13:44浏览次数:24  
标签:suf le P9576 TAOI int Ciallo kN ge operatorname

Link:https://www.luogu.com.cn/problem/P9576

知识点:Z 函数,哈希,枚举,数数,序列数据结构

本质上是个串串枚举题,但是用序列数据结构优化了一下(

你说的对,但是:::

昨天晚上看到室友在玩一个叫千恋万花的游戏,我就好奇的凑上去看了下。他好像在推一个巫女样子的角色,白头发顶着对兽耳,于是我凑上去和他分享,我说「原神里也有巫女,比你这个好看多了」。

我室友让我闭嘴,别影响他打游戏。我想了想的确,影响别人打游戏是不好的,于是我就不说话了。

后来我室友换了个叫什么茉子的角色,是个忍者的样子,我看到了这是真的忍不住了。我立刻手机打开了原神,冲上去和我室友说到「原神里也有好多当忍者的角色诶」,说着将手机拿过去想给他看看...

不知道这家伙是不是因为剧情推不下去就火气很大,在这时唰的一下站了起来,用手不礼貌的指着我的脸大喊,能不能别用你那破原神影响我。我听着他的话感到一阵羞辱,我好心和他分享,他却如此这般。

想着这样自私的室友,我也就没了和他争吵的念头,我就回自己的座位去了。随后我只是在位子上嘀咕着,柚子厨是这样的,他就跟火药桶被点炸了一样,立刻冲过来夺了我的手机。

他掐着我的手机怼着我的脸,姿势就像握着板砖一样,很是吓人。他假装义正言辞的跟我说,让我别拿原神这种游戏来恶心他。我低着头一眼不发,他也渐渐放下了持着我手机的手臂。

僵持了半分钟,他说他平时说话糙了点,但是对人一直很友善,刚刚他走了单身线才这么冲动,希望我别生气。我一时无言以对,就说道「没事,原神里优菈也是...」

我话还没说完,他青筋又暴起来,一下子把我的手机摔在了地上...

说真的,现在我才认清我室友自私自利的嘴脸,根本不值得我给他分享好东西。

简述

给定两个仅由小写字母构成的字符串 \(s, t\),求有多少组 \(l, r, l', r'\),满足下列条件:

  • \(1\le l\le r\le |s|\)。
  • 设 \(s\) 删去子串 \(s[l:r]\) 后变为 \(s'\),有:\(1\le l'\le r'\le |s'|\)。
  • \(s'[l':r'] = t\)。

\(1\le |s|\le 4\times 10^5\),\(1\le |t|\le 2\times 10^5\)。
2S,512MB。

分析

先解决一种简单的情况。若最终得到的 \(s'[l':r']\) 即为 \(s\) 中原来就是与 \(t\) 匹配的连续的子串 \(s[i:i + |t| - 1]\),则仅需确定删去的 \(l, r\),考虑 \(l,r\) 只可能同时出现在 \(s[i:i + |t| - 1]\) 的一侧且可以相等,显然方案数为 \(\frac{(i - 1)\times i}{2}+ \frac{(n - (i + |t| - 1))(n - (i + |t| - 1) + 1)}{2}\) 种。

然后考虑 最终得到的 \(s'[l':r']\) 在 \(s\) 中不连续的情况,即 \(s'[l':r']\) 可以被拆成 \(t\) 的一段前缀 \(\operatorname{pre}\) 和一段后缀 \(\operatorname{suf}\),两者在 \(s\) 中保持前后关系且不相邻,考虑枚举其中的一方并求可以拼成 \(t\) 的另一方的数量。

记 \(\operatorname{p}_1(i)\) 表示后缀 \(s[i:|s|]\) 与 \(t\) 的最长公共前缀长度,\(\operatorname{p}_2(i)\) 表示前缀 \(s[1:i]\) 与 \(t\) 的最长公共后缀长度。 \(\operatorname{p}_1\) 和 \(\operatorname{p} _2\) 可以通过对 \(s, t\) 正串和反串分别求 Z 函数预处理得到。为什么强调是前缀和后缀呢?因为要保证 \(\operatorname{pre}\) 和 \(\operatorname{suf}\) 都不能是空的,这样限制方便下一步求贡献。

则以 \(s_i\) 为开头可以组成长度为 \(1\sim \operatorname{p}_1(i)\) 的 \(\operatorname{pre}\)。对于以 \(s_i\) 开头长度为 \(l\) 的 \(\operatorname{pre}\),设可以与之拼成 \(t\) 的 \(\operatorname{suf}\) 以 \(s_j\) 为结尾,则 \(j\) 需要满足:

\[\begin{cases} j \ge i + |t|\\ \operatorname{p}_2(j)\ge |t| - \operatorname{p}_1(i) \end{cases}\]

对于一个满足 \(j\ge i + |t|\) 的 \(j\),考虑它可以对某个 \(i\) 产生多少贡献。为了保证 \(\operatorname{pre}\) 和 \(\operatorname{suf}\) 都不能是空的,则有贡献的以 \(s_j\) 为结尾的 \(\operatorname{suf}\) 的长度限制为:

\[|t| - \operatorname{p}_1(i)\le |\operatorname{suf}|\le \operatorname{p}_2(j) \]

由上式可知,对于所有以以 \(s_i\) 开头的 \(\operatorname{pre}\),所有满足上述条件的 \(\operatorname{suf}\) 数量之和,也即所有 \(j\) 对 \(i\) 的贡献之和即为:

\[\begin{aligned} &\sum_{j\ge i + |t|\land \operatorname{p}_2(j)\ge |t| - \operatorname{p}_1(i)} \operatorname{p}_2(j) - \left(|t| -\operatorname{p}_1(i)\right)\\ =& \left(\sum_{j\ge i + |t|\land \operatorname{p}_2(j)\ge |t| - \operatorname{p}_1(i)} \operatorname{p}_2(j)\right)- \left(|t| -\operatorname{p}_1(i)\right)\left(\sum_{j\ge i + |t|\land \operatorname{p}_2(j)\ge |t| - \operatorname{p}_1(i)} 1\right) \end{aligned}\]

上面括号里的两部分可以以相似的方式分别求出来。发现满足条件的 \(j\) 是一个二维偏序的形式,这东西显然可以通过序列数据结构维护,即考虑在倒序枚举 \(i\) 的同时维护两个初始为空的树状数组,枚举到 \(i(1\le i\le |s| - |t|)\) 时将位置 \(\operatorname{p}_2(i + |t|)\) 加上 \(\operatorname{p}_2(i + |t|)\text{ or } 1\),查询区间 \(\left[|t| - \operatorname{p}_1(i), |t| - 1\right]\) 中的权值和即得括号内的值。

总时间复杂度 \(O\left(|s|\log |t|\right)\) 级别。

所以这东西本质上就是个枚举嘛呃呃

代码

//Ciallo~(∠・ω< )⌒★
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 4e5 + 10;
//=============================================================
int n, m, z1[kN], z2[kN], p1[kN], p2[kN];
char s[kN], t[kN];
LL ans;
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); 
  return f * w;
}
struct Bit {
  #define lowbit(x) ((x)&-(x))
  int lim;
  LL t[kN];
  void Init(int n_) {
    lim = n_;
  }
  void Insert(int pos_, LL val_) {
    for (int i = pos_; i <= lim; i += lowbit(i)) {
      t[i] += val_;
    }
  }
  LL Sum(int pos_) {
    LL ret = 0;
    for (int i = pos_; i; i -= lowbit(i)) {
      ret += t[i];
    }
    return ret;
  }
  LL Query(int l_, int r_) {
    return Sum(r_) - Sum(l_ - 1);
  }
  #undef lowbit
} bit1, bit2;
void z_function() {
  z1[1] = m;
  for (int i = 2, l = 0, r = 0; i <= m; ++ i) {
    if (i <= r && z1[i - l + 1] < r - i + 1) {
      z1[i] = z1[i - l + 1];
    } else {
      z1[i] = std::max(0, r - i + 1);
      while (i + z1[i] <= m && t[z1[i] + 1] == t[i + z1[i]]) ++ z1[i];
    }
    if (i + z1[i] - 1 > r) l = i, r = i + z1[i] - 1;
  }
  for (int i = 1, l = 0, r = 0; i <= n; ++ i) {
    if (i <= r && z1[i - l + 1] < r - i + 1) {
      p1[i] = z1[i - l + 1];
    } else {
      p1[i] = std::max(0, r - i + 1);
      while (i + p1[i] <= n && s[i + p1[i]] == t[p1[i] + 1]) ++ p1[i];
    }
    if (i + p1[i] - 1 > r) l = i, r = i + p1[i] - 1;
  }

  std::reverse(t + 1, t + m + 1);
  std::reverse(s + 1, s + n + 1);
  z2[1] = m;
  for (int i = 2, l = 0, r = 0; i <= m; ++ i) {
    if (i <= r && z2[i - l + 1] < r - i + 1) {
      z2[i] = z2[i - l + 1];
    } else {
      z2[i] = std::max(0, r - i + 1);
      while (i + z2[i] <= m && t[z2[i] + 1] == t[i + z2[i]]) ++ z2[i];
    }
    if (i + z2[i] - 1 > r) l = i, r = i + z2[i] - 1;
  }
  for (int i = 1, l = 0, r = 0; i <= n; ++ i) {
    if (i <= r && z2[i - l + 1] < r - i + 1) {
      p2[i] = z2[i - l + 1];
    } else {
      p2[i] = std::max(0, r - i + 1);
      while (i + p2[i] <= n && s[i + p2[i]] == t[p2[i] + 1]) ++ p2[i];
    }
    if (i + p2[i] - 1 > r) l = i, r = i + p2[i] - 1;
  }
  std::reverse(p2 + 1, p2 + n + 1);
}
void Solve1() {
  for (int i = 1; i <= n; ++ i) {
    if (p1[i] == m) {
      int l1 = i - 1, l2 = n - (i + m) + 1;
      ans += 1ll * l1 * (l1 + 1) / 2ll;
      ans += 1ll * l2 * (l2 + 1) / 2ll;
    }
  }
}
void Solve2() {
  for (int i = 1; i <= n; ++ i) {
    if (p1[i] == m) -- p1[i];
    if (p2[i] == m) -- p2[i];
  }
  bit1.Init(m), bit2.Init(m);
  LL sum = 0;
  for (int i = n - m; i; -- i) {
    if (p2[i + m]) bit1.Insert(p2[i + m], 1);
    if (p2[i + m]) bit2.Insert(p2[i + m], p2[i + m]);

    LL ret1 = bit1.Query(m - p1[i], m - 1);
    LL ret2 = bit2.Query(m - p1[i], m - 1);
    sum = ret2 - ret1 * (m - p1[i] - 1);
    ans += sum;
  }
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  scanf("%s", s + 1); n = strlen(s + 1);
  scanf("%s", t + 1); m = strlen(t + 1);
  z_function();
  Solve1();
  Solve2();
  printf("%lld\n", ans);
  return 0;
}

标签:suf,le,P9576,TAOI,int,Ciallo,kN,ge,operatorname
From: https://www.cnblogs.com/luckyblock/p/17977133

相关文章

  • 洛谷 P9575 「TAOI-2」喵了个喵 Ⅳ 题解
    Solution先求出所有数的最大公约数\(d\),然后将每个数约去\(d\)。将约去后的数均分,约去前的数也均分。下文讨论的数都是约去\(d\)后的数(包括取的\(x\))。\(n\)为偶数,取\(x=1\),对半分即可。\(n\)不为偶数,且有奇数个偶数。取\(x=2\),设奇数和偶数分别有\(x,y\)个,B组取......
  • 「TAOI-2」Ciallo~(∠・ω< )⌒★ 题解
    「TAOI-2」Ciallo~(∠・ω<)⌒★题解不难发现,答案可以分成两种:整段的中间删一点,两端凑一起的考虑分开计算贡献。如果\(s\)中存在子串等于\(t\),那么自然,可以删左边的任何一段,或者右边的任何一段。不妨设子串开始的位置为\(i\),于是其贡献为\((1+2+\cdots+i......
  • P9574 「TAOI-2」Break Through the Barrier
    思路首先我们可以肯定的是,无论如何变化,答案最多比原序列的连续\(T\)的个数多\(2\)。理由很简单,对于\(...BT...TB...\),最好的可能就是前后两个\(B\)可以变成\(T\),因为只可能是\(BTTB\)变成\(TBBT\),所以变了以后再外面就一定是\(B\)了,且无法再变。所以我们可以先找到......
  • P9575 「TAOI-2」喵了个喵 Ⅳ
    思路考试的时候打死没想出来,一直在想暴力和质因数分解,我实在是太弱了,比赛后看了官方题解才恍然大悟,于是来蹭写篇题解。首先是一些特殊点:当\(n\)是偶数时,显然\(x\)可以取\(1\),这样\(\gcd\)就都是\(1\),然后随便平分就好了。恭喜你,你获得了\(2\)分。当\(n\)不是......
  • P9573 「TAOI-2」核心共振
    思路这道题最开始没发现数列必须是\(1,2,3,\cdots,n\),然后直接交了个输出\(n\)遍\(p\)的代码。我真的好蠢啊后面才发现这一点,于是开始思考,首先从\(p\)比较小的情况。如果\(p\)是\(1\)的话,那显然直接输出\(1,2,3,\cdots,n\)就好了。如果\(p\)是\(2\)的话,显然......
  • 「TAOI-2」Break Through the Barrier 题解
    前言:比赛前去做牙齿矫正,回来晚了10分钟……做比赛的运气全用在了一路绿灯上了(无语)。第二题切了两个半小时。决定写篇题解来抒发一下再记得愤怒愉悦之情。AC的想法很简单,就是表示出每一串连续的\(\texttt{T}\),其长度分别为\(l_1\liml_m\)。明显的,对于任何一个\(l_i\),在一......
  • 『STAOI』G - Round 3
    『STAOI』G-Round3因为在\(STAOI\)团里,所以赛时没打。\(T1\)luoguP9508『STA-R3』存在观察题意,手搓几组样例,易知符合题意的一组解形如\(a,b,b,c,b,b,……,z,(b),(b)\)。不会证明,可以参考下隔壁jijidawang的。时间复杂度\(O(n)\),可以通过本题。#include<bi......
  • 洛谷 P9221 「TAOI-1」Pentiment 题解
    Description给定\(n\timesm\)的矩阵,从第\(1\)行任意格子出发,每次向下、左、有走一步,有\(q\)个障碍不能经过,求走到第\(n\)行任意格子的方案。对于所有数据,\(1\leqn,m\leq10^9\),\(1\leqq\leq10^5\)。link:https://www.luogu.com.cn/problem/P9221Solution算法一考......
  • 『STAOI』G - Round 2 半个游记
    很刺激。2023.3.223:17第一次过审。2023.3.500:02第一次打回。原因是背锅人的链接又双叒叕挂错了(((2023.3.621:20第二次过审。2023.3.8邀请到国际著名设计师FoZwoK重新设计头图。这是base64。2023.3.8撤下比赛,决定打造rated。2023.3.13T4被偷走,拿去准备其它公......