首页 > 其他分享 >20240705

20240705

时间:2024-07-07 14:52:27浏览次数:8  
标签:qs 50005 20240705 int mid 后缀 dis

T1

NFLSOJ P5030 最小表示

考虑两个串本质相同的条件,发现如果计算出每一位上的字母距离它上一次出现的距离 \(dis_i\),那两个串本质相同等价于所有 \(dis_i\) 相同。注意到这个东西只和相对位置有关,所以只需要先对原串求一遍 \(dis\) 数组,然后对这个 \(dis\) 数组后缀排序一下,求出本质不同子串数即可。

但是这个事情很显然是不对的,因为比如说对于 \(\texttt{abcabcbac}\),对原串求 \(dis\) 得到 \(\{ 1, 2, 3, 2, 2, 2, 1, 3, 2 \}\),而如果考虑 \(\texttt{cbac}\) 这个后缀,\(dis\) 就变成了 \(\{ 1, 2, 3, 2 \}\)。容易发现这里对应位置上的 \(dis\) 值发生了变化。这实际上是由于后缀的起始点分隔了某些字母的相邻两次出现,从而导致后面那次出现的 \(dis\) 发生变化。注意到如果把这些发生变化的 \(dis\) 值全部变成 \(0\),也不会影响子串同构的判断。因此对于每个后缀直接把这些位置归零,然后把处理之后的后缀做后缀排序。注意,这里每个后缀是独立处理的,它们各自独立将自己的对应位置归零。

接下来考虑如何对处理之后的后缀做后缀排序。由于每个后缀是独立更改的,我们并不能很容易地完成排序。但是注意到字母只有 \(10\) 个,也就是 \(0\) 位置至多只有 \(10\) 个。因此可以对每个后缀记录该后缀的所有 \(0\) 位置,然后使用二分 + 哈希比较两个子串的字典序。如果没有删除位置,直接哈希即可。对于删除位置,由于数量很小,暴力枚举所有删除位置,将这一位对于哈希值的贡献减去即可。

完成后缀排序之后,直接根据 SA 求本质不同子串数的套路拿总共的减去每两个排名相邻的后缀的 LCP 即可。

代码
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
const int B = 233333;
const int P = 1000000007;
int a[50005], n, m;
string str;
int ap[15], aft[50005][15];
int del[50005][15];
int hsh[50005];
int pw[50005], lst[50005];
int ord[50005];
int Query(int l, int r) { return (P + hsh[r] - hsh[l - 1] * pw[r - l + 1] % P) % P; }
int chk(int l, int r) {
    int ret = Query(l, r);
    for (int i = 1; i <= del[l][0]; i++) {
        if (del[l][i] <= r) 
            ret = (P + ret - pw[r - del[l][i]] * a[del[l][i]] % P) % P;
    }
    return ret;
}
int lcp(int a, int b) {
    int l = 1, r = n - max(a, b) + 1, mid, ans = 0;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (chk(a, a + mid - 1) == chk(b, b + mid - 1)) 
            ans = mid, l = mid + 1;
        else 
            r = mid - 1;
    }
    return ans;
}
int q(int x, int y) {
    int ax = a[y];
    for (int i = 1; i <= del[x][0]; i++) del[x][i] == y ? (ax = 0) : 0;
    return ax;
}
bool cmp(int a, int b) {
    int ans = lcp(a, b);
    if (ans == n - max(a, b) + 1) 
        return a > b;
    else 
        return q(a, a + ans) < q(b, b + ans);
}
signed main() {
    freopen("string.in", "r", stdin);
    freopen("string.out", "w", stdout);
    cin >> n >> m;
    pw[0] = 1;
    for (int i = 1; i <= n; i++) pw[i] = pw[i - 1] * B % P;
    cin >> str;
    str = ' ' + str;
    for (int i = 1; i <= n; i++) {
        a[i] = i - ap[str[i] - 'a'];
        hsh[i] = (hsh[i - 1] * B + a[i]) % P;
        lst[i] = ap[str[i] - 'a'];
        ap[str[i] - 'a'] = i;
        for (int j = lst[i] + 1; j <= i; j++) aft[j][str[i] - 'a'] = i;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < m; j++) {
            if (aft[i][j]) 
                del[i][++del[i][0]] = aft[i][j];
        }
    }
    for (int i = 1; i <= n; i++) ord[i] = i;
    sort(ord + 1, ord + n + 1, cmp);
    int ans = n * (n + 1) / 2;
    for (int i = 1; i < n; i++) ans -= lcp(ord[i], ord[i + 1]);
    cout << ans << "\n";
    return 0;
}

T3

NFLSOJ P3361 模板性二维数点

考虑所有黑点的纵坐标都小于白点的纵坐标时会发生什么。此时我们选出的答案里必然包含黑点权值的最大值或白点权值的最大值。如果两个点可以同时选,则显然正确。否则对于任意一组解,把其中一个换成对应的权值最大值必然更优。

因此考虑对纵坐标分治,每次考虑纵坐标在分治中心之下的黑点和纵坐标在分治中心之上的白点,根据结论我们可以在这一层中选出 \(O(区间长度)\) 个有效(可能成为答案)的点对。分治有 \(O(\log n)\) 层,因此有用点对的级别在 \(O(n \log n)\)。这样就可以对每个询问离线做二维数点了。

代码
#include <iostream>
#include <algorithm>
#include <cassert>
#include <vector>
#include <map>
#define lowbit(x) ((x) & (-(x)))
using namespace std;
int n, q;
struct Point {
    int x, y, w;
} val[3000005], a[100005], b[100005];
int vcnt;
struct Query {
    int l, r, id;
} qs[500005];
vector<Point> va;
vector<Point> vb;
int d[1200005], dcnt;
map<int, int> mp;
void Solve(int l, int r, vector<Point> v1, vector<Point> v2) {
    if (v1.empty() || l >= r) 
        return;
    int mid = (l + r) >> 1;
    vector<Point> va1, va2, vb1, vb2;
    for (auto p : v1) (p.y <= mid ? va1 : va2).emplace_back(p);
    for (auto p : v2) (p.y <= mid ? vb1 : vb2).emplace_back(p);
    if (va1.size() && vb2.size()) {
        Point p1, p2;
        p1.w = p2.w = 0;
        for (auto p : va1) p.w > p1.w ? (p1 = p) : p;
        for (auto p : vb2) {
            p.w > p2.w ? (p2 = p) : p;
            val[++vcnt] = (Point) { p1.x, p.x, p1.w + p.w };
        }
        for (auto p : va1) p.x != p1.x ? (val[++vcnt] = (Point) { p.x, p2.x, p.w + p2.w }) : p1;
    }
    Solve(l, mid, va1, vb1);
    Solve(mid + 1, r, va2, vb2);
}
struct BIT {
    int bit[1000005];
    void add(int x, int y) { for (; x <= 1000000; x += lowbit(x)) bit[x] = max(bit[x], y); }
    int query(int x) {
        int ret = 0;
        for (; x; x -= lowbit(x)) ret = max(ret, bit[x]);
        return ret;
    }
} bit;
struct SBIT {
    int bit[1000005];
    void add(int x, int y) { for (; x; x -= lowbit(x)) bit[x] = max(bit[x], y); }
    int query(int x) {
        int ret = 0;
        for (; x <= 1000000; x += lowbit(x)) ret = max(ret, bit[x]);
        return ret;
    }
} bit2;
int ans[500005];
int main() {
    freopen("template.in", "r", stdin);
    freopen("template.out", "w", stdout);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i].x >> a[i].y >> a[i].w;
    }
    for (int i = 1; i <= n; i++) {
        cin >> b[i].x >> b[i].y >> b[i].w;
    }
    cin >> q;
    for (int i = 1; i <= q; i++) cin >> qs[i].l >> qs[i].r, qs[i].id = i;
    for (int i = 1; i <= n; i++) d[++dcnt] = a[i].x, d[++dcnt] = b[i].x;
    for (int i = 1; i <= q; i++) d[++dcnt] = qs[i].l, d[++dcnt] = qs[i].r;
    d[++dcnt] = 0;
    sort(d + 1, d + dcnt + 1);
    d[0] = 2147483647;
    mp[d[0]] = -n - q - 1;
    for (int i = 1; i <= dcnt; i++) d[i] != d[i - 1] ? (mp[d[i]] = mp[d[i - 1]] + 1) : 0;
    for (int i = 1; i <= n; i++) a[i].x = mp[a[i].x], b[i].x = mp[b[i].x];
    for (int i = 1; i <= q; i++) qs[i].l = mp[qs[i].l], qs[i].r = mp[qs[i].r];
    dcnt = 0;
    for (int i = 1; i <= n; i++) d[++dcnt] = a[i].y, d[++dcnt] = b[i].y;
    sort(d + 1, d + dcnt + 1);
    mp.clear();
    for (int i = 1; i <= dcnt; i++) d[i] != d[i - 1] ? (mp[d[i]] = mp[d[i - 1]] + 1) : 0;
    for (int i = 1; i <= n; i++) a[i].y = mp[a[i].y], b[i].y = mp[b[i].y];
    for (int i = 1; i <= n; i++) {
        va.emplace_back(a[i]);
        vb.emplace_back(b[i]);
    }
    Solve(1, n * 2, va, vb);
    sort(val + 1, val + vcnt + 1, [](Point a, Point b) { return a.x < b.x; });
    sort(qs + 1, qs + q + 1, [](Query a, Query b) { return a.l < b.l; });
    for (int i = 1, j = 1; i <= q; i++) {
        while (j <= vcnt && val[j].x < qs[i].l) bit2.add(val[j].y, val[j].w), ++j;
        ans[qs[i].id] = bit2.query(qs[i].r);
    }
    for (int i = q, j = vcnt; i; i--) {
        while (j > 0 && val[j].x > qs[i].l) bit.add(val[j].y, val[j].w), --j;
        ans[qs[i].id] = max(ans[qs[i].id], bit.query(qs[i].r));
    }
    for (int i = 1; i <= q; i++) cout << (ans[i] == 0 ? -1 : ans[i]) << "\n";
    return 0;
}

考虑可能对答案有贡献的元素。

标签:qs,50005,20240705,int,mid,后缀,dis
From: https://www.cnblogs.com/forgotmyhandle/p/18288501

相关文章

  • 20240705比赛总结
    T1酸碱度中和https://gxyzoj.com/d/hzoj/p/3731因为是要将一些数变为一个值,所以差相对小的一些数修改的会更少,所以可以先将原数组排序因为当x可以时,x+1必然可以,所以考虑二分接下来考虑到因为上下变动的都至多为m,所以开头和结尾的差必然不超过2m它就可以看作用一些长度为2m的......
  • 20240705总结(欧拉回路,构造)
    A-FairShareCF1634EFairShare题解:用二分图做的。首先如果一种颜色出现奇数次一定无解。否则对于一种颜色的点分组,每组两个之间连边,保证每种颜色平分。然后把每一个数组分成n[i]/2组,每组两个之间连边,保证每一个数组平分。这样一定连出的是二分图,黑白染色B-NecklaceCF......
  • 程序人生日记20240705|工作零食:米饭+十分米莲藕汁+饼干(减脂记录)
    程序员的工作饮食减脂记录打卡餐别:早餐零食详情:(同事给的不算统计内)零食名称:十分米莲藕汁1杯主食选择:全麦法棍。大致热量估算:莲藕汁约50卡,低脂法棍约100卡,总计约150卡。初始数据:体重:90公斤目标:80公斤完成情况:完成。程序员自律宣言:程序猿不可以土肥圆~零食库剩余情况:10......