2019 USP-ICMC K. Candies

鏈接

題意:

n 個數, 還有 L, R, 問有多少個本質不同的區間和在 L R 之間。

思路:

先考慮一個問題 , 長度爲 n 的字符串, 求本質不同的子串有多少個。

  • n * (n + 1 ) / 2 然後在減去 height[i] , 這種是求總的, 然後減去重複的。
  • sa 代表排名爲 i 的子串在的位置, 然後 height[i] 代表排名爲 i 的子串和排名爲 i - 1 的子串 重複的長度。
    所以排名爲 i 的子串可以構成多少個以sa[i] 爲開頭的本質不同的子串? 那就是 n - (sa[i] - 1)- height[i], 把所有子串的結果加起來就好了。這樣加起來就沒有重複的了。

在回到這個題:

首先, 讀入數據, 然後前綴和, 然後 把原有數據和前綴和離散化, 然後把 前綴和用主席樹存起來。

然後考慮怎麼把所有區間不重複的加起來。
把原始數據用後綴數組處理一下, 得到 sa, height,
對於排名爲 i 的串, 起始位置爲 sa[i] 和 排名 i - 1 的串重複 height 個,
在這裏插入圖片描述
所以問題轉化成, 我以 sa[i] 爲 起始點, 枚舉 j, j 在 [l, r] 區間裏面, 有多少 j 滿足sum[j] - sum[sa[i] - 1] 在 L R 之間,
所以轉化成了 有多少個 j , 滿足 sum[j] 在 sum[sa[i]-1] + L, sum[sa[i] - 1] + R 之間, 用主席樹查詢。

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+100;
typedef long long ll;
ll b[N*2],L,R,sum[N],tim;
int n,a[N];
int rt[N],ls[N*40],rs[N*40],val[N*40],cnt;
void update(int &now, int l, int r, int k){
	++cnt; ls[cnt] = ls[now];
	rs[cnt] = rs[now];
	val[cnt] = val[now] + 1; now = cnt;
	if (l + 1 == r) return;
	int mid = (l + r) >> 1;
	if (k < mid) update(ls[now], l, mid, k);
	else update(rs[now], mid, r, k);
}

int ask(int pre, int now, int l, int r, int ll, int rr){
	if (ll <= l && rr >= r - 1){
		return val[now] - val[pre];
	}
	int mid = (l + r) >> 1;
	if (ll >= mid) return ask(rs[pre], rs[now], mid, r, ll, rr);
	else if (rr < mid) return ask(ls[pre], ls[now], l, mid, ll, rr); 
	else return ask(rs[pre], rs[now], mid, r, ll, rr) + ask(ls[pre], ls[now], l, mid, ll, rr);

}
namespace SA{
    int y[N*2], x[N*2], c[N*2], sa[N], rk[N], height[N];
    void get_SA(int *s, int m) {
        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;
        }
    }
    void get_height(int *s) {
        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;//h[i]>=h[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; //h[i]=height[rk[i]];
        }
    }
};

int main(){
	scanf("%d%lld%lld",&n,&L,&R);
	for (int i = 1; i <= n; ++i){
		scanf("%d",&a[i]);
		sum[i] = sum[i-1] + a[i];
		b[++tim] = a[i];
		b[++tim] = sum[i];
	}
	sort(b+1,b+tim+1);
	tim = unique(b+1,b+tim+1) - b - 1;
	// for (int i = 1; i <= tim; ++i)
	// 	printf("%lld ",b[i]);
	// printf("\n");
	for (int i = 1; i <= n; ++i){
		a[i] = lower_bound(b+1,b+tim+1,a[i]) - b;
		int tmp = lower_bound(b+1,b+tim+ 1,sum[i]) - b;
		// printf("%lld %d\n",sum[i], tmp);
		rt[i] = rt[i-1];
		update(rt[i], 1, tim+1, tmp);	
	}

	SA::get_SA(a, tim+1);
	SA::get_height(a);
	int ans = 0,l,r;
	for (int i = 1; i <= n; ++i){
		l = lower_bound(b+1, b+tim+1,sum[SA::sa[i] - 1] + L) - b;
		r = upper_bound(b+1, b+tim+1,sum[SA::sa[i] - 1] + R) - b - 1;
		// printf("%d %d %d %d %d\n",i,l,r,SA::sa[i] - 1 + SA::height[i], n);
		if (l <= r)
			ans += ask(rt[SA::sa[i] - 1 + SA::height[i]], rt[n], 1, tim + 1, l, r);
	}
	printf("%d\n",ans);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章