Candies 【Gym - 102302K】【后缀数组+主席树】

Candies

 Gym - 102302K


  有N个数,我们要知道去取[l, r]连续的一连串数的和满足\large L \leq \sum_{i=l}^{r} a_{i} \leq R,现在想知道这样的子串有多少个,其中判断子串是否相同的方式是,如果他们长得相同,就是相同的,在不同位置的话,是不影响的。

两个子串被认为是相同的。


  于是乎,就是往字符串上靠拢了,既然相等就被认为是相等的,于是,一个字符串,和另一个字符串,如果他们的LCP(最长公共前缀)存在,于是这段前缀就有算重复的可能性,但是由于这个前缀是不可能一直就相等下去的,我们不如先进行桶排一下,然后就是对于所有的后缀他们按照前缀序来排列了,于是乎,如果目前sa排来看,这一个和下一个的重复部分,我们尽可能的放到后面去算这些重复的部分,先去计算没有重复到的后缀。

  这样,到最后一个后缀肯定是完整的算完了的。

  现在,如何维护?对于所有的a[i],我们可以离散化之后,将它看成字符,于是“相同”这个问题便被解决了。接下去,因为前缀和可以方便求解,我们先求一下到每个数的前缀和,接下去,我们要是查以某个数为起点的,权值和在L~R之间的,我们就可以去查L+sum[pos - 1] ~ R+sum[pos - 1]这部分了。但是,有些位置上可能存在重复项,所以我们用SA里面的height来维护一下,hight[i]求的是i和i-1这两个rank排名下的后缀的最长公共前缀长度,于是,我们避开这段最长公共前缀,我们从pos + hight查到结尾的N处。这样的按照时间戳来查询的过程,实际上,就是主席树了。

6 1 6
0 7 1 -2 3 5
ans=8
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 5e5 + 7;
struct SA
{
    int n, m;
    int s[maxN];
    int y[maxN], x[maxN], c[maxN], sa[maxN], rk[maxN], height[maxN];
    inline void get_SA()
    {
        for(int i=1; i<=m; i++) c[i] = 0;   //桶的初始化
        for(int i=1; i<=n; i++) ++c[x[i] = s[i]];
        for(int i=2; i<=m; i++) c[i] += c[i - 1];   //利用差分前缀和的思想知道每个关键字最多是在第几名
        for(int i=n; i>=1; i--) sa[c[x[i]]--] = i;
        for(int k=1; k<=n; k<<=1)
        {
            int num = 0;
            for(int i=n - k + 1; i<=n; i++) y[++num] = i;
            for(int i=1; i<=n; i++) if(sa[i] > k) y[++num] = sa[i] - k; //是否可以作为第二关键字
            for(int i=1; i<=m; i++) c[i] = 0;
            for(int i=1; i<=n; i++) c[x[i]]++;  //因为上一次循环已经求出这次的第一关键字了
            for(int i=2; i<=m; i++) c[i] += c[i - 1];
            for(int i=n; i>=1; i--) //在同一第一关键字下,按第二关键字来排
            {
                sa[c[x[y[i]]]--] = y[i];
                y[i] = 0;
            }
            swap(x, y);
            x[sa[1]] = 1; num = 1;
            for(int i=2; i<=n; i++)
            {
                x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++num;
            }
            if(num == n) break;
            m = num;
        }
    }
    inline void get_height()
    {
        int k = 0;
        for(int i=1; i<=n; i++) rk[sa[i]] = i;
        for(int i=1; i<=n; i++)
        {
            if(rk[i] == 1) continue;    //第一名的height为0
            if(k) k--;  //height[i] >= height[i - 1] - 1
            int j = sa[rk[i] - 1];
            while(j + k <= n && i + k <= n && s[i + k] == s[j + k]) k++;
            height[rk[i]] = k;
        }
    }
    inline void clear()
    {
        n = 0; m = 5e5 + 1;
    }
} sa;
int N, _UP, _US;
ll a[maxN], L, R, Lsan[maxN], sum[maxN], lsan_sum[maxN], ans;
int root[maxN], tot, tree[maxN * 30], lc[maxN * 30], rc[maxN * 30];
void update(int &now, int old, int l, int r, int qx)
{
    now = ++tot;
    tree[now] = tree[old] + 1; lc[now] = lc[old]; rc[now] = rc[old];
    if(l == r) return;
    int mid = HalF;
    if(qx <= mid) update(lc[now], lc[old], l, mid, qx);
    else update(rc[now], rc[old], mid + 1, r, qx);
}
int query(int root_L, int root_R, int l, int r, int ql, int qr)
{
    if(ql <= l && qr >= r) return tree[root_R] - tree[root_L];
    int mid = HalF;
    if(qr <= mid) return query(lc[root_L], lc[root_R], l, mid, ql, qr);
    else if(ql > mid) return query(rc[root_L], rc[root_R], mid + 1, r, ql, qr);
    else return query(lc[root_L], lc[root_R], l, mid, ql, qr) + query(rc[root_L], rc[root_R], mid + 1, r, ql, qr);
}
int main()
{
    scanf("%d%lld%lld", &N, &L, &R);
    sa.clear();
    sum[0] = 0;
    for(int i=1; i<=N; i++) { scanf("%lld", &a[i]); Lsan[i] = a[i]; sum[i] = sum[i - 1] + a[i]; lsan_sum[i] = sum[i]; }
    sort(Lsan + 1, Lsan + N + 1);
    _UP = (int)(unique(Lsan + 1, Lsan + N + 1) - Lsan - 1);
    for(int i=1; i<=N; i++)
    {
        a[i] = lower_bound(Lsan + 1, Lsan + _UP + 1, a[i]) - Lsan;
        sa.s[++sa.n] = (int)a[i];
    }
    sa.s[++sa.n] = _UP + 1;
    sa.get_SA();
    sa.get_height();
    sort(lsan_sum + 1, lsan_sum + N + 1);
    _US = (int)(unique(lsan_sum + 1, lsan_sum + N + 1) - lsan_sum - 1);
    for(int i=1; i<=N; i++)
    {
        sum[i] = lower_bound(lsan_sum + 1, lsan_sum + _US + 1, sum[i]) - lsan_sum;
        update(root[i], root[i - 1], 1, _US, (int)sum[i]);
    }
    ll now_state;
    for(int i=1, pos, high, l, r; i<=N; i++)
    {
        pos = sa.sa[i];
        high = sa.height[i + 1];
        now_state = lsan_sum[sum[pos - 1]];
        l = (int)(lower_bound(lsan_sum + 1, lsan_sum + _US + 1, L + now_state) - lsan_sum);
        r = (int)(upper_bound(lsan_sum + 1, lsan_sum + _US + 1, R + now_state) - lsan_sum - 1);
        if(l > r) continue;
        ans += query(root[pos + high - 1], root[N], 1, _US, l, r);
    }
    printf("%lld\n", ans);
    return 0;
}
发布了774 篇原创文章 · 获赞 932 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/qq_41730082/article/details/104244603