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

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

时间:2023-08-27 12:34:17浏览次数:43  
标签:TAOI int 题解 Ciallo long hI include sum mod

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

不难发现,答案可以分成两种:

  • 整段的

  • 中间删一点,两端凑一起的

考虑分开计算贡献。

如果 \(s\) 中存在子串等于 \(t\),那么自然,可以删左边的任何一段,或者右边的任何一段。

不妨设子串开始的位置为 \(i\),于是其贡献为 \((1 + 2 + \cdots + i - 1) + (1 + 2 + \cdots +(|s| - i - |t| + 1))\)。

接下来考虑中间删一点,两端凑一起的情况。

令 \(f_i\) 表示 \(s\) 从 \(i\) 开始与 \(t\) 的最长相同前缀的长度,\(g_i\) 表示 \(s\) 从 \(i\) 向前与 \(t\) 的最长相同后缀的长度。

NOTICE:由于需要排除第一种情况,所以对于 \(f, g\),都需要对于 \(|t| - 1\) 取 \(\min\)。

这部分可以通过哈希和二分完成(或者 Z 函数也行)

于是需要考虑 \(f, g\) 如何相互贡献。

不难发现,对于两个端点 \(i \le j\) 可以做出贡献,需要满足:

  • \(i + |t| - 1 \lt j\)。考虑中间必须删一点,所以必须严格小于,这样才不会重叠或者接在一起。

  • \(f_i + g_j \ge |t|\)。这样才能凑出完整的目标串。

那么其最终的贡献为 \(f_i + g_j - |t| + 1\)。

于是可以得到表达式:

\[\sum_{i = 1}^n \sum_{j = i + |t|}^n [f_i+ g_j \ge |t|](f_i + g_j - |t| + 1) \]

不难发现可以倒着扫一遍,然后利用树状数组求和即可。

考场上以防万一,我用的双哈希……但好像有点多余。

#include <iostream>
#include <algorithm>
#include <string>
#include <cmath>

using namespace std;
const int N = 4e5 + 7, BASE = 131, mod = 1331;

string s, t;

using hI = unsigned long long;
using hP = unsigned int;

hI sha[N], tha[N];
hI sha2[N], tha2[N];
hI ofs[N], ofs2[N];

hI shash(int l, int r) {
    hI sha1 = sha[r] - sha[l - 1] * ofs[r - l + 1];
    sha1 += (sha2[r] + mod - sha2[l - 1] * ofs2[r - l + 1] % mod) % mod;
    return sha1;
}

hI thash(int l, int r) {
    hI tha1 = tha[r] - tha[l - 1] * ofs[r - l + 1];
    tha1 += (tha2[r] + mod - tha2[l - 1] * ofs2[r - l + 1] % mod) % mod;
    return tha1;
} 

int f[N], g[N];

#define lowbit(i) (i & -i)
// 这是倒着的树状数组!
struct BIT {
    long long b[N];
    void update(int i, int x) {
        for (; i; i -= lowbit(i)) b[i] += x; 
    }
    long long query(int i) {
        long long r = 0;
        for(; i < N; i += lowbit(i)) r += b[i];
        return r;
    }
} cnt, sum;

long long get(long long x) {
    return (1 + x) * x / 2;
}

int main() {
    cin >> s >> t;

    int n = s.size(), m = t.size();
    for (int i = 1; i <= n; ++i) {
        sha[i] = sha[i - 1] * BASE + s[i - 1] - 2;
        sha2[i] = (sha2[i - 1] * 17 % mod + s[i - 1] - 2) % mod;
    }

    for(int i = 1; i <= m; ++i) {
        tha[i] = tha[i - 1] * BASE + t[i - 1] - 2;
        tha2[i] = (tha2[i - 1] * 17 % mod + t[i - 1] - 2) % mod;
    }

    ofs[0] = ofs2[0] = 1;
    for (int i = 1, ie = max(s.size(), t.size()); i <= ie; ++i) {
        ofs[i] = ofs[i - 1] * BASE;
        ofs2[i] = ofs2[i - 1] * 17 % mod; 
    }

    long long ans = 0;

    // 简单倍增
    int W = 1 << ((int)log2(t.size()) + 1);    
    for (int i = 1; i <= n; ++i) {
        f[i] = g[i] = -1;

        for (int w = W; w; w >>= 1) {
            if (i + f[i] + w - 1 <= n && 1 + f[i] + w - 1 <= m 
                && shash(i, i + f[i] + w - 1) == thash(1, 1 + f[i] + w - 1))
                f[i] += w;

            if (i - g[i] - w + 1 > 0 && m - g[i] - w + 1 > 0 
                && shash(i - g[i] - w + 1, i) == thash(m - g[i] - w + 1, m))
                g[i] += w;
        }

        if (f[i] >= m) ans += get(i - 1) + get(n - i - m + 1), --f[i];
    }

    for (int i = n; i > m; --i) {
        if (g[i] >= m) --g[i];
        cnt.update(g[i], 1);
        sum.update(g[i], g[i]);

        ans += sum.query(m - f[i - m]) + (f[i - m] - m + 1) * cnt.query(m - f[i - m]);
    }

    cout << ans << '\n'; 
}

标签:TAOI,int,题解,Ciallo,long,hI,include,sum,mod
From: https://www.cnblogs.com/jeefy/p/17646202.html

相关文章

  • YACS 2023年8月月赛 甲组 T2 直线整点 题解
    简单题,先二分出直线上$x$最小的点使得这个点在矩形内。然后不断跳,直到遇到整点。(实际上要用扩欧,但初测能过于是就偷个懒没写) 接着不断跳直到不符合条件。先$\sqrt{V}$个跳一下,跳完后再一个一个跳就不用写二分了多好。代码:#include<iostream>#defineintlonglongusi......
  • UVA908[Re-connecting Computer Sites]题解
    原题1.题意分析题意就是给你很多组数,对于每组数,有三组小数据。第一组小数据先输入一个n表示顶点数,然后再输入n-1条边表示初始边数。其它组小数据先输入一个数k,表示增加的边的数量,然后再输入k条边,表示增加的边。在输入第二组小数据时,要先把边清空,重新输入,但是边的数量不变。2.做......
  • 题解:城市
    题目链接你说得对,但是不如换根。换根是由原先的树形DP简单变换而来,故事发生在这道叫做《城市》的题目中,在这里你妄图求解每个点到树中其它所有节点的距离,即\(f_i=\sum_{j=1}^ndis_{i\toj}\)。可以一次dfs求解出\(f_{root}\),然后我们发现走过一条边\((u,v,w)\)会使......
  • LGR-156-Div.3 题解
    LGR-156-Div.3题解洛谷网校8月普及组月赛I&MXOIRound1&飞熊杯#2第一次AK一个比赛!而且排名这么靠前!!!T1宝箱题目链接思路注意到答案有两种情况。1.从原点走到\(a\),再从\(a\)走到\(b\),2.从原点走到\(b\),再从\(b\)走到\(a\)。取一个最小值即可。代码int......
  • react-pdf在部分iOS手机上加载pdf失败问题解决
    最近项目快结束了,测试提了一个bug,iOS手机上加载pdf一直在转圈,加载不出来内容。看到这个bug,在电脑上和安卓手机上没有问题,iOS手机中打开确实又问题,初步确定为app问题。我们的项目是集成在客户的app里的,可能是app内的WebView和Safari有一些差异导致的问题。首先直接在iOS手机上用a......
  • 力扣-2. 两数相加(C++题解)
    题目链接:https://leetcode.cn/problems/add-two-numbers/description/给你两个 非空的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字0之......
  • 关于自建Rustdesk 远程桌面服务器的公网访问:无法连接中继服务器的问题解决方法
    自建服务器位于内网时,内网客户端ID/中继的地址通常写成内网IP,外网客户端一般会用公网IP进行端口映射,但这样设置出现外网客户端无法连接中继服务器,但内网客户端使用正常的现象。经过半天的排查分析,当内网和外网填写的自定义服务器地址时,rust服务器无法识别出需要使用nat包的地址,默......
  • P1848 Bookshelf G 题解
    这是本蒟蒻写的第一篇题解(写不好请指出)很明显他是一道dp题,因为第i本书放哪里只跟前i-1本树的放法有关系。我们可以是定义f[i][j]表示放了i本书,最后一层书架是以第j本书开始的。那么有动态转移方程:\(f[i][i]=min(f[i-1][j])+hi,w[j]+...+w[i-1]<=L\)\(f[i][j]=f[i-1][j]+max(0......
  • 力扣-228. 汇总区间(C++题解)
    题目链接:https://leetcode.cn/problems/summary-ranges/description/给定一个 无重复元素的 有序整数数组\(nums\)。返回恰好覆盖数组中所有数字的最小有序区间范围列表 。也就是说,\(nums\)的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于\(......
  • P2151 [SDOI2009] HH去散步 题解
    传送门简要题意:有\(n\)个人,\(m\)条无向边,走\(e\)条边,满足条件若第\(i\)条边为\(u->v\)则第\(i+1\)条边不能是\(v->u\),问\(s->t\)的方案有多少个,取模45989。因为要满足题目关于边的条件,所以我们考虑点边互换。将\(u-v\)的无向边一分为二变成\(u->v,v->u\),第\(i\)条边记录两个变......