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 个,
2019 USP-ICMC K. Candies_第1张图片
所以问题转化成, 我以 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
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;
}

你可能感兴趣的:(#,后缀数组,#,主席树)