zoukankan      html  css  js  c++  java
  • BZOJ4516: [Sdoi2016]生成魔咒

    题目地址

    题目地址

    题解

    很棒的一道后缀数组题!
    其实就是对原串求出每一个前缀的本质不同子串的个数。
    求一个串的本质不同子串数是一个经典问题,其为(frac {n imes (n + 1)} 2 - sum ext{height[i]})
    考虑插入前缀的过程,这种情况下的( ext {sa})数组和( ext {height})数组是在不断改变的,这样就很不好统计了。
    那么不妨把原串翻转过来,这样加入一个字符其实就是加入一个新的后缀而已。
    而我们知道( ext{LCP(x,y)} = min_{k=x+1}^y { ext{height[k]}}),所以在后缀没有加全的情况下其实也是可以知道两个串的( ext {LCP})的,只需要预处理一个( ext{ST})表即可。
    考虑一下加入一个后缀对当前情况下( ext {height})数组的影响,在进行一次后缀排序后,假设其位置为(pos),那么只会影响到( ext {height[pos]})( ext {height[pos + 1]})
    所以只需要动态修改这两个东西即可。
    这两个东西可以用平衡树来维护。具体的,在每次插入一个数后,找出它的前驱和后继,给(sum ext{height[i]})加上( ext{LCP}(pre, pos))( ext{LCP}(pos, next))
    平衡树可以上( ext{set})

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N = 100010;
    
    ll ans = 0;
    int n, m, a[N], b[N], f[N][18], LG[N];
    int sa[N], height[N], rnk[N], tp[N], tong[N];
    set<int> s;
    
    void radix_sort() {
    	for(int i = 1; i <= m; ++i) tong[i] = 0;
    	for(int i = 1; i <= n; ++i) tong[rnk[i]]++;
    	for(int i = 1; i <= m; ++i) tong[i] += tong[i - 1];
    	for(int i = n; i; --i) sa[tong[rnk[tp[i]]]--] = tp[i];
    }
    
    void suffix_sort() {
    	for(int i = 1; i <= n; ++i) rnk[i] = a[i], tp[i] = i;
    	m = n; radix_sort();
    	for(int w = 1, p = 1; p < n && w <= n; w <<= 1, m = p) {
    		p = 0; 
    		for(int i = 1; i <= w; ++i) tp[++p] = n - w + i;
    		for(int i = 1; i <= n; ++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 <= n; ++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 <= n; ++i) {
    		if(k) --k;
    		int j = sa[rnk[i] - 1];
    		while(a[i + k] == a[j + k] && i + k <= n && j + k <= n) ++k;
    		height[rnk[i]] = k; 
    	}
    }
    
    int query(int l, int r) {
    	int k = LG[r - l + 1];
    	return min(f[l][k], f[r - (1 << k) + 1][k]);
    }
    
    int main() {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), b[i] = a[i];
    	sort(b + 1, b + n + 1);
    	for(int i = 1; i <= n; ++i) a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
    	reverse(a + 1, a + n + 1); suffix_sort();
    	LG[2] = 1;
    	for(int i = 3; i <= n; ++i) LG[i] = LG[i >> 1] + 1;
    	for(int j = 1; j <= n; ++j) f[j][0] = height[j];
    	for(int j = 1; j <= 19; ++j) {
    		for(int i = 1; i + (1 << j) - 1 <= n; ++i) 
    			f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
    	}
    	ans = 0;
    	for(int i = n; i; --i) {
    		set<int>::iterator it = s.insert(rnk[i]).first, p1 = it, p2 = it;
    		if(it != s.begin()) {
    			p1--; 
    			ans += query(*p1 + 1, rnk[i]); 
    			if(++p2 != s.end()) {
    				ans += query(rnk[i] + 1, *p2);
    				ans -= query(*p1 + 1, *p2);
    			}
    		} else if(++p2 != s.end()) 
    			ans += query(rnk[i] + 1, *p2);
    		printf("%lld
    ", 1LL * (n - i + 1) * (n - i + 2) / 2LL - ans);
    	}
    	return 0;
    } 
    
  • 相关阅读:
    Android获取当前时间的3中方法总结
    解决 C# .NET WebClient WebRequest请求缓慢的问题
    无刷新的批量图片上传插件.NET版
    <img>标签显示本地路径的图片的.NET解决方案
    无刷新分页
    Shader基本类型
    shader内置变量和函数
    Shader基础
    Lua中的基本函数库
    Lua中的操作系统库
  • 原文地址:https://www.cnblogs.com/henry-1202/p/12045734.html
Copyright © 2011-2022 走看看