zoukankan      html  css  js  c++  java
  • 【BZOJ2754】[SCOI2012]喵星球上的点名

    【BZOJ2754】[SCOI2012]喵星球上的点名

    题面

    bzoj

    洛谷

    题解

    这题有各种神仙做法啊,什么暴力(AC)自动机、(SAM)等等五花八门

    我这个蒟蒻在这里提供一种复杂度正确且常数小的做法。

    根据后缀数组经典套路,

    我们用一个未出现过的字符将所有串连接起来求一边(SA)(不算询问串)

    然后因为我们现在已经将所有后缀排好序了

    而询问串要有贡献一定是一个后缀的前缀

    所以一个区间能产生贡献的后缀是排名连续的一段

    这样这段区间的左右端点可以二分出来

    我们再对于每个位置记一下它是哪一个人的

    这样第一问就被转化成了区间内有多少种颜色,直接上莫队在排名上跑即可

    到了这一步

    第二问就很好做了

    我们考虑差分,每新遇到一类数,我们加上剩余询问个数的贡献

    每去掉一类数,减去剩余询问个数的贡献即可

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring> 
    #include <cmath> 
    #include <algorithm>
    using namespace std; 
    inline int gi() { 
    	register int data = 0, w = 1; 
    	register char ch = 0; 
    	while (!isdigit(ch) && ch != '-') ch = getchar(); 
    	if (ch == '-') w = -1, ch = getchar(); 
    	while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar(); 
    	return w * data; 
    } 
    const int MAX_N = 2e5 + 5; 
    int N, n, m, a[MAX_N], id[MAX_N], lim = 1e4 + 5;
    int sa[MAX_N]; 
    void GetSA() { 
    #define cmp(i, j, k) (y[i] == y[j] && y[i + k] == y[j + k])
        static int x[MAX_N], y[MAX_N], bln[MAX_N];
        int M = lim; 
        for (int i = 1; i <= N; i++) bln[x[i] = a[i]]++; 
        for (int i = 1; i <= M; i++) bln[i] += bln[i - 1]; 
        for (int i = N; i >= 1; i--) sa[bln[x[i]]--] = i; 
        for (int k = 1; k <= N; k <<= 1) { 
            int p = 0; 
            for (int i = 0; i <= M; i++) y[i] = 0; 
            for (int i = N - k + 1; i <= N; i++) y[++p] = i; 
            for (int i = 1; i <= N; i++) if (sa[i] > k) y[++p] = sa[i] - k;
            for (int i = 0; i <= M; i++) bln[i] = 0; 
            for (int i = 1; i <= N; i++) bln[x[y[i]]]++; 
            for (int i = 1; i <= M; i++) bln[i] += bln[i - 1]; 
            for (int i = N; i >= 1; i--) sa[bln[x[y[i]]]--] = y[i]; 
            swap(x, y); x[sa[1]] = p = 1;
            for (int i = 2; i <= N; i++) x[sa[i]] = cmp(sa[i], sa[i - 1], k) ? p : ++p;
            if (p >= N) break;
            M = p; 
        } 
    } 
    const int LEN = 400; 
    int cnt[MAX_N], bln[MAX_N]; 
    struct Query { int l, r, id; } q[MAX_N]; int q_cnt = 0;
    inline bool operator < (const Query &a, const Query &b) { 
    	if (bln[a.l] ^ bln[b.l]) return a.r < b.r; 
    	else return (bln[a.l] & 1) ? a.r < b.r : a.r > b.r; 
    }
    int A1, ans1[MAX_N], ans2[MAX_N]; 
    
    void add(int x, int pos) { 
    	cnt[id[sa[x]]]++; 
    	if (cnt[id[sa[x]]] == 1) ++A1, ans2[id[sa[x]]] += q_cnt - pos + 1; 
    } 
    void del(int x, int pos) { 
    	cnt[id[sa[x]]]--; 
    	if (cnt[id[sa[x]]] == 0) --A1, ans2[id[sa[x]]] -= q_cnt - pos + 1; 
    } 
    int main () {
    #ifndef ONLINE_JUDGE 
    	freopen("cpp.in", "r", stdin); 
    #endif 
    	n = gi(), m = gi(); 
    	for (int i = 1, L; i <= n; i++) { 
    		L = gi(); for (int j = 1; j <= L; j++) a[++N] = gi(), id[N] = i; a[++N] = ++lim; 
    		L = gi(); for (int j = 1; j <= L; j++) a[++N] = gi(), id[N] = i; a[++N] = ++lim; 
    	} 
    	GetSA(); 
    	for (int i = 1; i <= m; i++) {
    		int L = 1, R = N; 
    		for (int len = gi(), j = 1; j <= len; ++j) { 
    			int x = gi(), l = L, r = R; 
    			while (l <= r) {
                    int mid = (l + r) >> 1; 
    				if (a[sa[mid] + j - 1] < x) l = mid + 1; 
    				else r = mid - 1; 
    			}
    			int tmp = l; l = L, r = R; 
    			while (l <= r) { 
    				int mid = (l + r) >> 1; 
    				if (a[sa[mid] + j - 1] <= x) l = mid + 1; 
    				else r = mid - 1; 
    			} 
    			L = tmp, R = r; 
    		} 
    		if (L <= R) q[++q_cnt] = (Query){L, R, i}; 
    	} 
    	for (int i = 1; i <= N; i++) bln[i] = (i - 1) / LEN + 1; 
    	sort(&q[1], &q[q_cnt + 1]); 
    	for (int ql = 1, qr = 0, i = 1; i <= q_cnt; i++) { 
    		while (ql < q[i].l) del(ql, i), ++ql; 
    	    while (ql > q[i].l) --ql, add(ql, i); 
    	    while (qr < q[i].r) ++qr, add(qr, i); 
    	    while (qr > q[i].r) del(qr, i), --qr;
    		ans1[q[i].id] = A1; 
    	}
    	for (int i = 1; i <= m; i++) printf("%d
    ", ans1[i]); 
    	for (int i = 1; i <= n; i++) printf("%d ", ans2[i]); 
    	return 0;  
    } 
    

    跑得还是挺快的,在洛谷跑了(Rank4)

  • 相关阅读:
    Linux普通用户登录后,命令行提示:-bash-4.1$ ,原因分析及解决
    ps命令
    Linux命令ping
    Linux 系统管理 : last 命令详解
    Linux命令head
    Linux more与less命令
    Mac下开启Chrome非安全模式
    mac host 修改
    mac 电脑记
    a的样式顺序
  • 原文地址:https://www.cnblogs.com/heyujun/p/10305951.html
Copyright © 2011-2022 走看看