7.9 独居日 莫队相关

7.9 独居日

每当宿舍里只有我一个人的时候,就要与蚊子大战3个小时...脑壳疼...

后缀数组

先背代码:

int n;
char str[1000010];
int sa[1000010], SA[1000010], rk[1000010], RK[1000010], bac[1000010], alp;

int main() {
	cin >> str + 1;
	n = strlen(str + 1);
	alp = 256;
	for (int i = 0; i <= alp; ++i)
		bac[i] = 0;
	for (int i = 1; i <= n; ++i)
		bac[str[i]] ++;
	for (int i = 1; i <= alp; ++i)
		bac[i] += bac[i - 1];
	for (int i = 1; i <= n; ++i)
		sa[bac[str[i]]--] = i;
	for (int i = 1; i <= n; i++)
		rk[sa[i]] = rk[sa[i - 1]] + (str[sa[i]] != str[sa[i - 1]]);
	for (int p = 1; p <= n; p <<= 1) {
		for (int i = 1; i <= n; ++i)
			bac[rk[sa[i]]] = i;
		for (int i = n; i >= 1; --i)
			if (sa[i] > p)
				SA[bac[rk[sa[i] - p]]--] = sa[i] - p;
		for (int i = n; i > n - p; --i)
			SA[bac[rk[i]] --] = i;

#define cmp(x, y) (rk[x] != rk[y] || rk[x + p] != rk[y + p])

		for (int i = 1; i <= n; ++i)
			RK[SA[i]] = RK[SA[i - 1]] + cmp(SA[i], SA[i - 1]);
		for (int i = 1; i <= n; ++i)
			sa[i] = SA[i], rk[i] = RK[i];
		if (rk[sa[n]] >= n)
			break;
	}
	for (int i = 1; i <= n; ++i)
		cout << sa[i] << " ";

	return 0;
}

本质不同的子串个数

回到我们的话题。我们还有一个数组没求,那就是height。height要怎么求呢?其实很简单,因为有性质:height[rank[i]] ≥height[rank[i−1]]−1。

证明: 如果 height[rank[i−1]] = 0 显然成立,否则在 i−1 这个后缀与它前驱的 LCP 的基础上去掉第一个字符,就找到了一个后缀与 i 这个后缀的 LCP≥height[rank[i−1]]−1。

于是我们能线性求height。

h[i] = lcp(sa[i], sa[i - 1])

带修莫队

这样的一种排序方法[左块增右块增时间增]

左端点在同一块并且右端点在同一块的,按照时间递增排序;

否则,左端点在同一块的,按照右端点所在块递增排序;

否则,按照左端所在块递增排序。

最优块长

不过胡试了几组,发现当按照[奇偶块右端点迂回增减]排序时,块长等于 \(n^{\frac{2}{3}}\)的时候得分高(指P1903 [国家集训队]数颜色 / 维护队列);当按照[左块增右块增时间增]排序时,块长等于 \(\sqrt{n * m}\) 得分高。以及块长2000对这两种排序方式都比较适用。

练习 P1494 [国家集训队]小Z的袜子

只需要记录当前区间内的袜子总数Sum和每种袜子的数量colnum[n]

总的选择方案数即为\(Sum * (Sum - 1) / 2\),合法的选择方案数即为\(\sum (colnum[i] * (colnum[i] - 1) / 2)\)

然后正常跑莫队即可,排序用奇偶块增减迂回排序。

注意

  • 分子分母只要一个为零就要特判,不能约分了,直接答案存0/1。这里卡了好久

代码

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 50006;
int n, m, a[N], num[N], bel[N], t, L, R, colnum[N];
ll Sum, sum0, ss;
struct P {
	int id, l, r;
} p[N];
ll ans, Ans[N][2], g;

bool cmp(P A, P B){
    return bel[A.l] == bel[B.l] ? (bel[A.l] & 1 ? A.r < B.r : A.r > B.r) : bel[A.l] < bel[B.l];
}
 
bool cmp0(P a, P b) {
	return a.r < b.r;
}

void work(const int l, const int r){
    for(int i = l; i <= r; ++i){
        colnum[a[i]]++; Sum++;
    }
    for(int i = 1; i <= n; ++i) if(colnum[i] > 0)   sum0 += 1ll * colnum[i] * (colnum[i] - 1) / 2;
    return ;
}

void Add(const int Pos){
    Sum++;
    if(colnum[a[Pos]] > 0)  sum0 -= 1ll * colnum[a[Pos]] * (colnum[a[Pos]] - 1) / 2;
    colnum[a[Pos]]++;
    sum0 += 1ll * colnum[a[Pos]] * (colnum[a[Pos]] - 1) / 2;
    return;
}
void Del(const int Pos){
    Sum--;
    sum0 -= 1ll * colnum[a[Pos]] * (colnum[a[Pos]] - 1) / 2;
    colnum[a[Pos]]--;
    if(colnum[a[Pos]] > 0)  sum0 += 1ll * colnum[a[Pos]] * (colnum[a[Pos]] - 1) / 2;;
    return;
}

ll gcd(ll a, ll b) {
	return b ? gcd(b, a % b) : a;
}

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d", &p[i].l, &p[i].r);
		p[i].id = i;
	}
	t = pow(n, 1.0 / 3); t *= t;
	if(t < 10) t = 10;
	for(int i = 1; i <= n; ++i)    bel[i] = i / t + 1;
	sort(p + 1, p + m + 1, cmp);
	L = p[1].l; R = p[1].r; 
	work(L, R);
	for (int i = 1; i <= m; i++) {
		while(L > p[i].l) Add(--L);
		while(R < p[i].r) Add(++R);
		while(L < p[i].l) Del(L++);
        while(R > p[i].r) Del(R--);
        if(Sum == 0 || sum0 == 0){
            Ans[p[i].id][0] = 0;
            Ans[p[i].id][1] = 1;
        }
        else{
            ss = Sum * (Sum - 1) / 2;
            g = gcd(sum0, ss);
            Ans[p[i].id][0] = sum0 / g;
            Ans[p[i].id][1] = ss / g;
        }
	}
	for (int i = 1; i <= m; i++)
		printf("%lld/%lld\n", Ans[i][0], Ans[i][1]);
	return 0;
}
posted @ 2021-07-09 21:35  咕咕坤  阅读(36)  评论(1编辑  收藏  举报