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

    题目地址

    BZOJ2754: [SCOI2012]喵星球上的点名

    题解

    做法:后缀数组+ST表+树状数组。
    先把所有串拼在一起,处理出SA数组,然后分别考虑两个问题。

    对于第一个问题,首先对于每个询问,能够对答案有贡献的串一定在连续的一个区间且包含询问串(在经过后缀排序之后)。因为( ext {LCP(x,y)} = min_{k=x+1}^y{ ext{height[k]}}),所以可以先预处理一下ST表,然后二分找出对每个询问有贡献的区间,即({ k| ext{LCP(k,i)}geq ext{length}_k })
    那么现在的问题就转变成了一个非常经典的问题,给出(m)个询问,每次询问区间([l,r])的颜色数。
    这个问题直接离线树状数组即可。(将询问按右端点排序,然后处理出(pre)数组表示当前位置的数上一次出现的位置,每次在树状数组中加入当前数的同时删掉该数的(pre),就可以正确得到区间颜色数了)。

    对于第二个问题,和第一个问题本质基本相同,转化为求每个点被多少个询问区间覆盖。这个东西差分+离线+树状数组即可解决。
    总复杂度是(O(n log n))的。当然这题的做法很多,有将问题转化后(O(n sqrt n))莫队求解的,也有直接大力上AC自动机的。不过目前我会的只有后缀数组相关做法...

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 500010;
    
    int a[N], n, m, len, bl[N], T = 10001, length[N], st[N], L[N];
    int sa[N], height[N], tong[N], rnk[N], tp[N], ans[N], pre[N];
    struct Q {int l, r, id;} q[N];
    bool cmp(Q a, Q b) {return a.r < b.r;}
    
    void radix_sort() {
    	for(int i = 1; i <= m; ++i) tong[i] = 0;
    	for(int i = 1; i <= len; ++i) tong[rnk[i]]++;
    	for(int i = 1; i <= m; ++i) tong[i] += tong[i - 1];
    	for(int i = len; i; --i) sa[tong[rnk[tp[i]]]--] = tp[i];
    }
    
    void suffix_sort() {
    	for(int i = 1; i <= len; ++i) rnk[i] = a[i], tp[i] = i;
    	m = T; radix_sort();
    	for(int w = 1, p = 1; p < len && w <= len; w <<= 1, m = p) {
    		p = 0; 
    		for(int i = 1; i <= w; ++i) tp[++p] = len - w + i;
    		for(int i = 1; i <= len; ++i) if(sa[i] > w) tp[++p] = sa[i] - w;
    		radix_sort(); swap(tp, rnk); rnk[sa[1]] = p = 1;
    		for(int i = 2; i <= len; ++i) 
    			rnk[sa[i]] = (tp[sa[i]] == tp[sa[i - 1]] && tp[sa[i] + w] == tp[sa[i - 1] + w]) ? p : ++p;
    	}
    	for(int i = 1, k = 0; i <= len; ++i) {
    		if(k) --k;
    		int j = sa[rnk[i] - 1];
    		while(a[i + k] == a[j + k] && i + k <= len && j + k <= len) ++k;
    		height[rnk[i]] = k; 
    	}
    }
    
    namespace ST {
    int f[N][25], LG[N];
    int query(int l, int r) {
    	int k = LG[r - l + 1];
    	return min(f[l][k], f[r - (1 << k) + 1][k]);
    }
    void init() {
    	for(int i = 1; i <= len; ++i) f[i][0] = height[i];
    	for(int i = 2; i <= len; ++i) LG[i] = LG[i >> 1] + 1;
    	for(int j = 1; j <= 24; ++j) 
    		for(int i = 1; i + (1 << j) - 1 <= len; ++i) 
    			f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
    }
    }
    
    namespace BIT {
    int c[N];
    #define lowbit(i) (i & -i)
    void clear() {memset(c, 0, sizeof(c));}
    void add(int x, int v) {
    	if(!x) return;
    	for(int i = x; i <= len; i += lowbit(i)) c[i] += v;
    }
    int query(int x) {
    	if(!x) return 0;
    	int ans = 0;
    	for(int i = x; i; i -= lowbit(i)) ans += c[i];
    	return ans;
    }
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("1.in", "r", stdin);
    	freopen("qwq.out", "w", stdout);
    #endif
    	int M; scanf("%d%d", &n, &M);
    	for(int i = 1; i <= n; ++i) {
    		int x, tmp; scanf("%d", &x);
    		while(x--) {
    			scanf("%d", &tmp); 
    			a[++len] = tmp; bl[len] = i;
    		}
    		a[++len] = ++T;
    		scanf("%d", &x);
    		while(x--) {
    			scanf("%d", &tmp);
    			a[++len] = tmp; bl[len] = i; 
    		}
    		a[++len] = ++T;
    	}
    	for(int i = 1; i <= M; ++i) {
    		int x, tmp; scanf("%d", &x);
    		length[i] = x;
    		st[i] = len + 1;
    		while(x--) {
    			scanf("%d", &tmp);
    			a[++len] = tmp;
    		}
    		a[++len] = ++T;
    	}
    	suffix_sort(); ST::init();
    	for(int i = 1; i <= M; ++i) {
    		int l = rnk[st[i]], r = len;
    		q[i].l = q[i].r = rnk[st[i]];
    		while(l <= r) {
    			int mid = (l + r) >> 1;
    			if(ST::query(rnk[st[i]] + 1, mid) >= length[i]) l = mid + 1, q[i].r = mid;
    			else r = mid - 1;
    		}
    		l = 1, r = rnk[st[i]];
    		while(l <= r) {
    			int mid = (l + r) >> 1;
    			if(ST::query(mid + 1, rnk[st[i]]) >= length[i]) r = mid - 1, q[i].l = mid;
    			else l = mid + 1;
    		}
    		q[i].id = i;
    	}
    	memset(tong, 0, sizeof(tong));
    	for(int i = 1; i <= len; ++i) {
    		if(bl[sa[i]] > 0) {
    			pre[i] = tong[bl[sa[i]]];
    			tong[bl[sa[i]]] = i;
    		}
    	}
    	sort(q + 1, q + M + 1, cmp);
    	int cur = 1; BIT::clear(); 
    	for(int i = 1; i <= len; ++i) {
    		if(bl[sa[i]]) {BIT::add(pre[i], -1); BIT::add(i, 1);}
    		while(cur <= M && q[cur].r == i) {
    			ans[q[cur].id] = BIT::query(q[cur].r) - BIT::query(q[cur].l - 1);
    			++cur;
    		}
    	}
    	for(int i = 1; i <= M; ++i) printf("%d
    ", ans[i]);
    	memset(ans, 0, sizeof(ans));
    	for(int i = 1; i <= M; ++i) L[i] = q[i].l;
    	sort(L + 1, L + M + 1); cur = 1; int k = 1; 
    	BIT::clear();
    	for(int i = 1; i <= len; ++i) {
    		while(cur <= M && L[cur] == i) BIT::add(i, 1), ++cur;
    		if(bl[sa[i]] > 0) ans[bl[sa[i]]] += BIT::query(i) - BIT::query(pre[i]);
    		while(k <= M && q[k].r == i) BIT::add(q[k++].l, -1);
    	}
    	for(int i = 1; i <= n; ++i) printf("%d ", ans[i]); puts("");
    	return 0;
    }
    
  • 相关阅读:
    LeetCode重建二叉树系列问题总结
    LeetCode二叉树的前序、中序、后序遍历(递归实现)
    Redis持久化方式的选择
    Redis客户端——Jedis的使用
    Redis初识
    Java并发编程面试题 Top 50 整理版
    @GeneratedValue源码解析
    @Controller和@RestController源码解析
    Spring Boot整合Mybatis并完成CRUD操作
    Redis数据结构和常用API
  • 原文地址:https://www.cnblogs.com/henry-1202/p/12056188.html
Copyright © 2011-2022 走看看