zoukankan      html  css  js  c++  java
  • [SDOI2016]生成魔咒

    Description

    BZOJ 4516
    Luogu 4070
    逐次从字符串尾添加字符,求每次加入后的本质不同的子串数。

    Solution

    首先翻转原字符串,这样前缀就变成了后缀。
    离线后模拟逐次加入字符的过程。加入第(i)个字符时,会增加(i)个子串,设已加入的字符中,(i)的前驱为(pre),后继为(suc)
    会有(lcp(pre, i)+lcp(i, suc))个重复子串,但是原来的(lcp(pre, suc))已经不算在重复子串中了。

    Code

    #include <cstdio>
    #include <algorithm>
    #include <set>
    
    typedef long long ll;
    const int N = 1e5 + 10;
    
    int s[N], a[N], h[N], sa[N], rnk[N], tax[N], tmp[N], fr[N];
    int n, m;
    int tr[N<<2];
    std::set<int> fck;
    
    void rsort() {
        for (int i = 1; i <= m; ++i) tax[i] = 0;
        for (int i = 1; i <= n; ++i) ++tax[rnk[i]];
        for (int i = 1; i <= m; ++i) tax[i] += tax[i-1];
        for (int i = n; i >= 1; --i) sa[tax[rnk[tmp[i]]]--] = tmp[i];
    }
    
    void ssort() {
        for (int i = 1; i <= n; ++i) rnk[i] = a[i], tmp[i] = i;
        rsort();
        for (int w = 1, p = 0; p < n; w <<= 1) {
            p = 0;
            for (int i = 1; i <= w; ++i) tmp[++p] = n - w + i;
            for (int i = 1; i <= n; ++i) if (sa[i] > w) tmp[++p] = sa[i] - w;
            rsort();
            std::swap(rnk, tmp);
            rnk[sa[1]] = p = 1;
            for (int i = 2; i <= n; ++i) {
                rnk[sa[i]] = (tmp[sa[i]] == tmp[sa[i-1]] && tmp[sa[i]+w] == tmp[sa[i-1]+w])
                            ? p : ++p;
            }
            m = p;
        }
        for (int i = 1, k = 0; i <= n; ++i) {
            while (a[i+k] == a[sa[rnk[i]-1]+k]) ++k;
            h[rnk[i]] = k;
            if (k) --k;
        }
    }
    
    inline int ls(int o) { return o<<1; }
    inline int rs(int o) { return o<<1|1; }
    
    void build(int o, int l, int r) {
    	
    	if (l == r) {
    		tr[o] = h[l];
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(ls(o), l, mid); build(rs(o), mid+1, r);
    	tr[o] = std::min(tr[ls(o)], tr[rs(o)]);
    }
    
    int query(int o, int l, int r, int ql, int qr) {
    	if (ql <= l && r <= qr)
    		return tr[o];
    	int mid = (l+r) >> 1;
    	int ans = 0x3f3f3f3f;
    	if (ql <= mid) ans = std::min(ans, query(ls(o), l, mid, ql, qr));
    	if (qr > mid) ans = std::min(ans, query(rs(o), mid+1, r, ql, qr));
    	return ans;
    }
    
    ll lcp(int x, int y) {
        if (x == y) return 0;
        if (x > y) std::swap(x, y);
    	return (ll)query(1, 1, n, x+1, y);
    }
    
    int pre(int x) {
    	std::set<int>::iterator it = fck.lower_bound(x);
    	if (it == fck.begin()) return -1;
    	return *(--it);
    }
    int suc(int x) {
        std::set<int>::iterator it = fck.upper_bound(x);
        if (it == fck.end()) return -1;
        return *(it);
    }
    
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; ++i) scanf("%d", &s[i]), tmp[i] = s[i];
    	
    	std::sort(s+1, s+n+1);
    	m = std::unique(s+1, s+n+1) - s - 1;
    	for (int i = 1; i <= n; ++i)
    	    a[n-i+1] = std::lower_bound(s+1, s+m+1, tmp[i]) - s;
    
    	ssort();
    	build(1, 1, n);
    	
    	ll ans = 0;
    	for (int i = n; i >= 1; --i) {
    	    ans += (ll)n - i + 1;
    	    fck.insert(rnk[i]);
    	    int pr = pre(rnk[i]), sc = suc(rnk[i]);
    	    ans -= lcp(pr, rnk[i]) + lcp(rnk[i], sc) - lcp(pr, sc);
    	    printf("%lld
    ", ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    [转载]从程序员到项目经理:思维一换天地宽
    针对后台TCP服务F5健康检查配置
    [转载]生活在 Emacs 中
    [转载]为何 Emacs 和 Vim 被称为两大神器
    Emacs文件命令
    功能点估算速记
    [转载]CMMI之功能点估算法:EI、EQ和EO
    一些有用的 Emacs 配置(窗口快速切换、一键透明效果、任意位置删除整行等)
    refiling失败报错Invalid function: org-preserve-local-variables
    Cognos定时Email发送报表数据功能
  • 原文地址:https://www.cnblogs.com/wyxwyx/p/9581241.html
Copyright © 2011-2022 走看看