JZOJ ???? platform

没有传送门
题目大意

给你一个长度为 n 的由小写字符组成的字符串。每个位置(不是每种字符)有一个权值。定义它的某个子串的排名为:把原串中所有子串按字典序降序排序并去重后这个子串所在的位置。称一个子串是我们想要的,当且仅当:这个子串的排名等于这个子串的权值之和。求在所有 n 2 2 个子串中,有多少个子串是我们想要的。要求输出这些子串。

n 200000 ;权值非负,保证权值之和不会溢出;保证答案不超过 200000

Limited Constraint:
1. n 50
2. n 1000
3. n 50000

Special Instance:
1. 字符串只由一种字符组成;
2. 所有位置权值相同。

考场上的思路

n 50 估计是把所有子串拿出来 sort 吧……我没管它,由于时间不多,就没有做 Special Instance 了,只做了 n 1000 的情况。

由于题目要求去重,因此考虑后缀数组。对于 S A i 的某个子串,如果在 h e i g h t i 范围内,那么它的排名就是之前算过的排名;否则它的排名从左到右,从上到下依次递增。当我遇到一个新的子串时(即遇到一个 h e i g h t i 范围外的子串时),我直接暴力向下查找与它相等的子串,并更新答案。这样每个子串会被计算一次,时间复杂度为 O ( n 2 )

思路

首先肯定要用后缀数组(你用 SAM 当我没说,但是这种要去重的应该后缀数组很好做吧,关键是接下的步骤 SAM 不好做),所以第一步是对的。注意到这么一个问题:平常后缀排序都是升序,这里的降序是个什么鬼?

注意到权值非负,也就是说前缀和非负。考虑这么一个问题:当子串左端点确定时,右端点越往右,那么这个子串字典序越大,排名越小;但是前缀和却越来越大。换句话说,对于一个左端点,至多只会有一个我们想要的子串……

考虑二分排名的前缀和的位置,那么现在的问题就是如何求一个子串的排名。似乎没有一个好的在线求子串排名的方法,考虑离线。自然想到按 SA 从小到大的顺序处理,因为 SA 上子串的排名是依次的(就是暴力的那种方法)。如果对于每个后缀我们能够维护每个前缀的排名,问题就解决了。

观察从 S A i S A i + 1 排名是如何变化的。在 h e i g h t i 范围内,排名不会变化;在 h e i g h t i 范围外,排名会从当前的计数器开始依次递减:这是一个等差数列。考虑用线段树维护这个东西,显然可以 O ( n log n ) 区间赋值和区间加等差数列。线段树维护的排名也应该是从左到右递减的,因此可以直接在线段树上二分。

虽然这里说是区间加等差数列,但实际上我们的操作时先清空,再区间加等差数列,相当于让区间等于一个等差数列,因此可以方便地维护最小值,也就可以二分了。由于我们直接在线段树上二分,因此时间复杂度为 O ( n log n ) 。实际操作时,只需要写一个区间赋值等差数列即可。

参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef int INT_PUT;
INT_PUT readIn()
{
    INT_PUT a = 0; bool positive = true;
    char ch = getchar();
    while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
    if (ch == '-') { positive = false; ch = getchar(); }
    while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
    return positive ? -a : a;
}
void printOut(INT_PUT x)
{
    char buffer[20]; int length = 0;
    if (x < 0) putchar('-'); else x = -x;
    do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
    do putchar(buffer[--length]); while (length);
}

const int maxn = int(2e5) + 5;
int n;
char str[maxn];
int val[maxn];

struct SuffixArray
{
    int temp[maxn];
    int x[maxn];
    int y[maxn];
    int buf_size;
    int buf[maxn];

    int SA[maxn];
    int Rank[maxn];
    int Height[maxn];
    void GetSA()
    {
        buf_size = 128;
        int* rank = x;
        int* SA_second = y;
        for (int i = 0; i < n; i++)
            rank[i] = str[i];

        for (int i = 0; i < buf_size; i++) buf[i] = 0;
        for (int i = 0; i < n; i++) buf[rank[i]]++;
        for (int i = 1; i < buf_size; i++) buf[i] += buf[i - 1];
        for (int i = n - 1; ~i; i--) SA[--buf[rank[i]]] = i;

        for (int k = 1; k <= n; k <<= 1)
        {
            int t = 0;
            for (int i = n - k; i < n; i++)
                SA_second[t++] = i;
            for (int i = 0; i < n; i++)
                if (SA[i] >= k)
                    SA_second[t++] = SA[i] - k;

            for (int i = 0; i < buf_size; i++) buf[i] = 0;
            for (int i = 0; i < n; i++) buf[rank[SA_second[i]]]++;
            for (int i = 1; i < buf_size; i++) buf[i] += buf[i - 1];
            for (int i = n - 1; ~i; i--)
                SA[--buf[rank[SA_second[i]]]] = SA_second[i];

            int* oldRank = rank;
            std::swap(rank, SA_second);
            rank[SA[0]] = 0;
            t = 1;
            for (int i = 1; i < n; i++)
                rank[SA[i]] = (oldRank[SA[i - 1]] == oldRank[SA[i]] &&
                    SA[i - 1] + k < n && SA[i] + k < n &&
                    oldRank[SA[i - 1] + k] == oldRank[SA[i] + k]) ? t - 1 : t++;
            if (t >= n)
                break;
            buf_size = t;
        }
    }
    void GetHeight()
    {
        for (int i = 0; i < n; i++)
            Rank[SA[i]] = i;
        int same = 0;
        for (int i = 0; i < n; i++)
        {
            if (Rank[i])
            {
                if (same) same--;
                int pre = SA[Rank[i] - 1];
                while (i + same < n && pre + same < n &&
                    str[i + same] == str[pre + same])
                    same++;
            }
            else
                same = 0;
            Height[Rank[i]] = same;
        }
    }
} suffix_array;

int* sa;
int* height;
std::vector<std::pair<int, int> > ans;

#define RunInstance(x) delete new x
struct brute
{
    brute()
    {
        LL all = 0;
        for (int i = 0; i < n; i++)
            all += n - sa[i] - height[i];

        LL rank = 0;
        for (int i = 0; i < n; i++)
        {
            for (int j = height[i]; j < n - sa[i]; j++)
            {
                rank++;
                int k = i;
                do
                {
                    if (val[sa[k] + j + 1] - val[sa[k]] == all - rank + 1)
                        ans.push_back(std::make_pair(sa[k] + 1, sa[k] + j + 1));
                    k++;
                } while (k < n && height[k] > j);
            }
        }
    }
};
struct work
{
    class SegTree
    {
        static inline int code(int l, int r)
        {
            return (l + r) | (l != r);
        }
        struct Node
        {
            LL min;
            bool bFlag;
            LL a;
            int d;
            Node() : min(), bFlag(), a(), d() {}
        } nodes[maxn * 2];

        int g_L, g_R, g_Pos, g_Val;
        void cover(int l, int r, LL a, int d)
        {
            Node& t = nodes[code(l, r)];
            t.min = a + (r - l) * d;
            t.bFlag = true;
            t.a = a;
            t.d = d;
        }
        void pushdown(int l, int r)
        {
            Node& t = nodes[code(l, r)];
            if (t.bFlag)
            {
                int mid = (l + r) >> 1;
                cover(l, mid, t.a, t.d);
                cover(mid + 1, r, t.a + t.d * (mid - l + 1), t.d);
                t.bFlag = false;
            }
        }
        void update(int l, int r)
        {
            Node& t = nodes[code(l, r)];
            int mid = (l + r) >> 1;
            Node& lc = nodes[code(l, mid)];
            Node& rc = nodes[code(mid + 1, r)];
            t.min = std::min(lc.min, rc.min);
        }
        void modify_(int l, int r, LL a)
        {
            if (g_L <= l && r <= g_R)
            {
                cover(l, r, a, g_Val);
                return;
            }
            pushdown(l, r);
            int mid = (l + r) >> 1;
            if (g_R <= mid)
                modify_(l, mid, a);
            else if (g_L > mid)
                modify_(mid + 1, r, a);
            else
            {
                modify_(l, mid, a);
                modify_(mid + 1, r, a + g_Val * (mid - std::max(l, g_L) + 1));
            }
            update(l, r);
        }
        void query_(int l, int r)
        {
            if (l == r)
            {
                Node& t = nodes[code(l, r)];
                if (val[g_Pos + l] - g_Val == t.min)
                    ans.push_back(std::make_pair(g_Pos + 1, g_Pos + l));
                return;
            }
            pushdown(l, r);
            int mid = (l + r) >> 1;
            if (g_R <= mid)
                return void(query_(l, mid));
            Node& lc = nodes[code(l, mid)];
            if (lc.min > val[g_Pos + mid] - g_Val)
                query_(mid + 1, r);
            else
                query_(l, mid);
        }

    public:
        void modify(int l, int r, LL a, int d)
        {
            g_L = l;
            g_R = r;
            g_Val = d;
            modify_(1, n, a);
        }
        void query(int r, int base, int sub)
        {
            g_L = 1;
            g_R = r;
            g_Pos = base;
            g_Val = sub;
            query_(1, n);
        }
    } st;

    work()
    {
        LL all = 0;
        for (int i = 0; i < n; i++)
            all += n - sa[i] - height[i];

        for (int i = 0; i < n; i++)
        {
            st.modify(height[i] + 1, n - sa[i], all, -1);
            st.query(n - sa[i], sa[i], val[sa[i]]);
            all -= n - sa[i] - height[i];
        }
    }
};

void run()
{
    scanf("%s", str);
    n = strlen(str);
    suffix_array.GetSA();
    suffix_array.GetHeight();
    sa = suffix_array.SA;
    height = suffix_array.Height;

    for (int i = 1; i <= n; i++)
        val[i] = val[i - 1] + readIn();

    if (n <= 1000)
        RunInstance(brute);
    else
        RunInstance(work);

    std::sort(ans.begin(), ans.end());
    printOut(ans.size());
    putchar('\n');
    for (int i = 0; i < ans.size(); i++)
    {
        printOut(ans[i].first);
        putchar(' ');
        printOut(ans[i].second);
        putchar('\n');
    }
}

int main()
{
#ifndef LOCAL
    freopen("platform.in", "r", stdin);
    freopen("platform.out", "w", stdout);
#endif
    run();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lycheng1215/article/details/80954043