• 【BZOJ4560】[NOI2016]优秀的拆分


    【BZOJ4560】[NOI2016]优秀的拆分

    题面

    bzoj

    洛谷

    题解

    考虑一个形如(AABB)的串是由两个形如(AA)的串拼起来的

    那么我们设

    (f[i]):以位置(i)为结尾的形如(AA)串的个数

    (g[i]):以位置(i)为开头的形如(AA)串的个数

    [ herefore Ans=sum_{i=1}^nf[i]*g[i+1] ]

    题目的难点转化为求(f,g)

    但是,其实我们只要(O(n^2))暴力求一下就有(95pts)了,

    所以我们接下来考虑最后的(5pts)怎么拿:

    我们枚举(A)的长度(len)

    将所有位置为(len)的倍数的点设为关键点,

    则如果一个(AA)满足要求

    这个(AA)必过两个关键点,

    那么我们要算的就是相邻两个关键点对答案的贡献:

    记相邻两个关键点为(,i,j)那么(j=i+len)

    (Lcp=lcp(suf(i), suf(j)),Lcs=lcs(pre(i-1),pre(j-1)))

    那么,如果(Lcp+Lcs<len),则不能构成(AA)

    为什么呢?

    相当于这样一种情况:

    [underbrace{.......i-1}_{Lcs};overbrace{underbrace{i........}_{Lcp};....underbrace{.......j-1}_{Lcs}}^{len};underbrace{j........}_{Lcp} ]

    这样子是不合法的。

    反之,中间两段的(Lcp,Lcs)会有交,而我们这个(A)串的终点落在中间长度为(Lcp+Lcs-len+1)的交上都是可以的

    因为这样的话平移一下可以保证紧跟着出现一个不重叠的(A)

    又因为串(A)起点和终点分别出现的位置是一段区间,所以直接分别在(f,g)上差分即可

    复杂度是调和级数(O(nlogn))

    具体细节看代码:

    #include <iostream> 
    #include <cstdio> 
    #include <cstdlib> 
    #include <cstring> 
    #include <cmath> 
    #include <algorithm> 
    #include <map> 
    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 = 3e4 + 5;
    char a[MAX_N]; 
    int N, lg[MAX_N], f[MAX_N], g[MAX_N]; 
    struct SuffixArray { 
    	int sa[MAX_N], rnk[MAX_N], lcp[MAX_N]; 
    	void buildSA() { 
            #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];
    		memset(sa, 0, sizeof(sa)); 
    		memset(rnk, 0, sizeof(rnk)); 
    		memset(lcp, 0, sizeof(lcp)); 
    		memset(x, 0, sizeof(x)); 
    		memset(y, 0, sizeof(y)); 
    		memset(bln, 0, sizeof(bln));
    		int M = 122; 
    		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; 
    		} 
    		for (int i = 1; i <= N; i++) rnk[sa[i]] = i; 
    		for (int i = 1, j = 0; i <= N; i++) {
    			if (j) j--; 
    			while (a[i + j] == a[sa[rnk[i] - 1] + j]) ++j; 
    			lcp[rnk[i]] = j; 
    		} 
    	} 
    	int st[16][MAX_N]; 
    	void buildST() { 
    		memset(st, 63, sizeof(st)); 
    		for (int i = 1; i <= N; i++) st[0][i] = lcp[i]; 
    		for (int i = 1; i <= 15; i++)
    			for (int j = 1; j <= N; j++)
    				st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]); 
    	} 
    	int query(int l, int r) { 
    		int _l = l, _r = r; 
    		l = min(rnk[_l], rnk[_r]) + 1, r = max(rnk[_l], rnk[_r]);
    		int t = lg[r - l + 1]; 
    		return min(st[t][l], st[t][r - (1 << t) + 1]); 
    	} 
    } A, B; 
    void Sol() {
    	scanf("%s", a + 1); N = strlen(a + 1); 
    	A.buildSA(), A.buildST(); 
    	reverse(&a[1], &a[N + 1]); 
    	B.buildSA(), B.buildST(); 
    	memset(f, 0, sizeof(f)); 
    	memset(g, 0, sizeof(g)); 
    	for (int Len = 1; Len <= N / 2; Len++) { 
    		for (int i = Len, j = i + Len; j <= N; i += Len, j += Len) { 
    			int Lcp = min(A.query(i, j), Len), Lcs = min(B.query(N - i + 2, N - j + 2), Len - 1); 
    			int t = Lcp + Lcs - Len + 1; 
    			if (Lcp + Lcs >= Len) {
    				g[i - Lcs]++, g[i - Lcs + t]--; 
    				f[j + Lcp - t]++, f[j + Lcp]--; 
    			} 
    		} 
    	} 
    	for (int i = 1; i <= N; i++) f[i] += f[i - 1], g[i] += g[i - 1]; 
    	long long ans = 0; 
    	for (int i = 1; i < N; i++) ans += 1ll * f[i] * g[i + 1]; 
    	printf("%lld
    ", ans); 
    } 
    int main () { 
    #ifndef ONLINE_JUDGE 
        freopen("cpp.in", "r", stdin); 
    #endif 
    	for (int i = 2; i <= 30000; i++) lg[i] = lg[i >> 1] + 1; 
    	int T; scanf("%d", &T);
    	while (T--) Sol(); 
    	return 0; 
    } 
    
  • 相关阅读:
    c# 清楚表格美容
    linux下svn迁移目录操作
    XML 操作(判断用户登录)
    confirm弹出一个包含"确定"与"取消"的对话方块.
    Js获取当前日期时间及其它操作
    简单实用方法!!
    随机数概率解决思路
    简单的SQL语句!!
    JS验证表单大全
    C语言系列(三):最近重拾C语言的想法,谈到C中易错点,难点;以及开源代码中C语言的一些常用技巧,以及如何利用define、typedef、const等写健壮的C程序
  • 原文地址:https://www.cnblogs.com/heyujun/p/10360053.html
Copyright © 2020-2023  润新知