zoukankan      html  css  js  c++  java
  • SvT[BZOJ3879]

    【题目描述】
    有一个长度为n的仅包含小写字母的字符串(S),下标范围为([1,n]).

    现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在(S)中出现的起始位置来表示),求这些后缀两两之间的LCP(LongestCommonPrefix)的长度之和.一对后缀之间的LCP长度仅统计一遍.

    【输入格式】
    第一行两个正整数(n,m),分别表示(S)的长度以及询问的次数.

    接下来一行有一个字符串(S).

    接下来有(m)组询问,对于每一组询问,均按照以下格式在一行内给出:

    首先是一个整数(t),表示共有多少个后缀.接下来(t)个整数分别表示(t)个后缀在字符串(S)中的出现位置.

    【输出格式】
    对于每一组询问,输出一行一个整数,表示该组询问的答案.由于答案可能很大,仅需要输出这个答案对于(23333333333333333)(一个巨大的质数)取模的余数

    题解

    这不是和 差异「AHOI2013」 几乎一模一样吗。。。那题就相当于询问是(1sim n)的所有后缀 然后式子稍微变变形

    传送门

    这题也是一样 先建好后缀数组 对于每组询问 先按照后缀数组的(rnk)进行排序 然后去重 然后用ST表来求出排序后相邻两个后缀的LCP

    剩下的单调栈DP就跟上面那题一样了 不想再敲一遍了 请点上面传送门 看解法一:后缀数组

    唯一的区别就是多个ST表。。。

    #include <bits/stdc++.h>
    #define N 500005
    using namespace std;
    typedef long long ll;
    
    const ll mod = 23333333333333333;
    int n, m, t;
    char s[500005];
    int sa[N], sum[N], rnk[N], sa2[N], key[N], height[N], q[N], tmp[N], stk[N], top;
    ll f[N], ans;
    
    inline bool check(int *num, int a, int b, int l) {
    	return num[a] == num[b] && num[a+l] == num[b+l];
    }
    
    inline void DA(int _m) {
    	int i, j, p; 
    	for (i = 1; i <= _m; i++) sum[i] = 0;
    	for (i = 1; i <= n; i++) sum[rnk[i]=s[i]]++;
    	for (i = 2; i <= _m; i++) sum[i] += sum[i-1];
    	for (i = n; i >= 1; i--) sa[sum[rnk[i]]--] = i; 
    	for (j = 1, p = 0; j <= n; j <<= 1, _m = p) {
    		p = 0; for (i = n - j + 1; i <= n; i++) sa2[++p] = i;
    		for (i = 1; i <= n; i++) if (sa[i] > j) sa2[++p] = sa[i] - j;
    		for (i = 1; i <= n; i++) key[i] = rnk[sa2[i]];
    		for (i = 1; i <= _m; i++) sum[i] = 0;
    		for (i = 1; i <= n; i++) sum[key[i]]++;
    		for (i = 2; i <= _m; i++) sum[i] += sum[i-1];
    		for (i = n; i >= 1; i--) sa[sum[key[i]]--] = sa2[i];
    		for (swap(rnk, sa2), p = 2, rnk[sa[1]] = 1, i = 2; i <= n; i++) {
    			rnk[sa[i]] = check(sa2, sa[i-1], sa[i], j) ? p - 1 : p++;
    		}
    	}
    }
    
    inline void geth() {
    	ll p = 0;
    	for (int i = 1; i <= n; i++) rnk[sa[i]] = i;
    	for (int i = 1; i <= n; i++) {
    		if (p) p--;
    		int j = sa[rnk[i]-1];
    		while (s[i + p] == s[j + p]) p++;
    		height[rnk[i]] = p;
    	}
    }
    
    int st[N][21];
    
    void preST() {
    	for (int i = 1; i <= n; i++) st[i][0] = height[i];
    	for (int l = 1; (1 << l) <= n; l++) {
    		for (int i = 1; i <= n - (1 << l) + 1; i++) {
    			st[i][l] = min(st[i][l-1], st[i+(1<<(l-1))][l-1]);
    		}
    	}
    }
    
    inline int query(int x, int y) {
    	int l = log2(y - x + 1);
    	return min(st[x][l], st[y - (1 << l) + 1][l]);
    }
    
    inline bool cmp(int x, int y) {
    	return rnk[x] < rnk[y];
    }
    
    int main() {
    	scanf("%d %d %s", &n, &m, s + 1);
    	DA(128); 
    	geth();
    	preST();
    	for (int i = 1; i <= m; i++) {
    		scanf("%d", &t);
    		ans = 0;
    		for (int j = 1; j <= t; j++) scanf("%d", &q[j]);
    		sort(q + 1, q + t + 1, cmp);
    		t = unique(q + 1, q + t + 1) - q - 1;
    		for (int j = 2; j <= t; j++) {
    			tmp[j] = query(rnk[q[j-1]] + 1, rnk[q[j]]);
    		}
    		stk[top=1] = 1; 
    		for (int j = 2; j <= t; j++) {
    			while (top && tmp[stk[top]] > tmp[j]) top--;
    			f[j] = f[stk[top]] + (j - stk[top]) * tmp[j];
    			stk[++top] = j;
    			ans += f[j];	
    		}
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Vue-router笔记
    webpack及其配置
    高阶函数+组件化开发
    Es6语法+v-on参数相关+vue虚拟dom
    英语资源及其APP推荐
    Qt获取ip地址
    QT创建和使用动态链接库
    error C2664: “strcmp”: 不能将参数 1 从“WCHAR [260]”转换为“const char *”
    Vue|VUE router 导航重复点击报错的问题解决方案
    Vue|触发路由跳转
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM62.html
Copyright © 2011-2022 走看看