zoukankan      html  css  js  c++  java
  • 「hihocoder1413 Rikka with String」

    题目

    哈哈哈哈哈哈哈哈哈哈我还没自闭

    好像前后调了两天了

    哈哈哈哈哈哈哈哈哈哈我还没自闭

    这道题就是给定一个小写字母串,回答分别把每个位置上的字符替换为(#)后的本质不同的子串数

    首先就是跨过这个特殊字符的字符串出现次数显然都是(1),这部分的贡献就直接是(i imes(n-i+1))

    之后我们用(SAM)搞出所有前缀和所有后缀的本质不同子串个数,这时候答案的贡献就是(pre_{i-1}+beh_{i+1})

    显然会算多一些在前缀和后缀里都出现的子串

    想个办法把这些东西搞出来

    我们维护出每个等价类(endpos)的最大值(mx[i])和最小值(mi[i])

    显然如果特殊字符插入在([mi[i],mx[i]-len[i]])里的话会使得这个字符串在左右两边都被算过

    如果特殊字符插入在([mx[i]-len[i]+1,mx-len[fa[i]]),发现这里好像需要一个每往后移动一个位置就会少多算一个子串,那就是一个公差为(-1)的等差数列啊,二阶差分维护一下就好了

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define LL long long
    #define re register
    #define maxn 600005
    char S[maxn>>1];
    struct SAM{
    	int len[maxn],fa[maxn],son[maxn][26];
    	LL tot;int lst,cnt;
    	inline void ins(int c) {
    		int p=++cnt,f=lst;lst=p;
    		len[p]=len[f]+1;
    		while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
    		if(!f) {fa[p]=1;tot+=len[p]-len[fa[p]];return;}int x=son[f][c];
    		if(len[f]+1==len[x]) {fa[p]=x;tot+=len[p]-len[fa[p]];return;}
    		int y=++cnt;tot-=len[x]-len[fa[x]];
    		len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y;
    		tot+=len[y]-len[fa[y]],tot+=len[p]-len[fa[p]],tot+=len[x]-len[fa[x]];
    		for(re int i=0;i<26;i++) son[y][i]=son[x][i];
    		while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
    	}
    }S1,S2;
    int n;
    LL pre[maxn],beh[maxn];
    int len[maxn],fa[maxn],son[maxn][26],mx[maxn],mi[maxn];
    int lst=1,cnt=1;
    int tax[maxn>>1],a[maxn];
    LL c[maxn],t[maxn];
    inline void extend(int c,int o) {
    	int p=++cnt,f=lst;lst=p;
    	len[p]=len[f]+1,mx[p]=mi[p]=o;
    	while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
    	if(!f) {fa[p]=1;return;}
    	int x=son[f][c];
    	if(len[f]+1==len[x]) {fa[p]=x;return;}
    	int y=++cnt;len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y;
    	for(re int i=0;i<26;i++) son[y][i]=son[x][i];
    	while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
    }
    int main() {
    	scanf("%d",&n);scanf("%s",S+1);S1.lst=S1.cnt=1;S2.lst=S2.cnt=1;
    	for(re int i=1;i<=n;i++) S1.ins(S[i]-'a'),pre[i]=S1.tot;
    	for(re int i=n;i;i--) S2.ins(S[i]-'a'),beh[i]=S2.tot;
    	memset(mi,20,sizeof(mi));
    	for(re int i=1;i<=n;i++) extend(S[i]-'a',i);
    	for(re int i=1;i<=cnt;i++) tax[len[i]]++;
    	for(re int i=1;i<=n;i++) tax[i]+=tax[i-1];
    	for(re int i=cnt;i;--i) a[tax[len[i]]--]=i;
    	for(re int i=cnt;i;--i) {
    		int x=a[i];
    		mx[fa[x]]=max(mx[fa[x]],mx[x]);mi[fa[x]]=min(mi[fa[x]],mi[x]);
    	}
    	for(re int i=2;i<=cnt;i++) {
    		if(mi[i]==mx[i]) continue;
    		int L=mx[i]-len[i]+1,R=mx[i]-len[fa[i]]-1;
    		if(L<=mi[i]+1) {
    			L=mi[i]+1;int li=R-L+1;
    			if(L>R) continue;
    			t[L]+=li;
    			t[L+1]+=-1-li;
    			t[R+2]+=1;
    			continue;
    		}
    		c[mi[i]+1]+=len[i]-len[fa[i]];c[L]-=len[i]-len[fa[i]];
    		if(len[i]-len[fa[i]]>1) {
    			if(L>R) continue;
    			t[L]+=len[i]-len[fa[i]]-1;
    			t[L+1]+=-len[i]+len[fa[i]];
    			t[R+2]+=1;
    		} 
    	}
    	for(re int i=1;i<=n;i++) t[i]+=t[i-1];
    	for(re int i=1;i<=n;i++) c[i]+=c[i-1]+t[i];
    	for(re int i=1;i<=n;i++) 
    		printf("%lld ",pre[i-1]+beh[i+1]-c[i]+(LL)i*(LL)(n-i+1));
    	puts("");
    	return 0;
    }
    
  • 相关阅读:
    Linux关闭防火墙命令
    js改变数组的两个元素的位子,互换、置顶
    vue nexttick的理解和使用场景
    vue mint-ui 框架下拉刷新上拉加载组件的使用
    vue项目中使用了vw适配方案,引入第三方ui框架mint-ui时,适配问题解决
    小程序开发笔记【二】,抽奖结果json数据拼装bug解决
    gulp插件gulp-nunjucks-render的使用及gulp4的简单了解
    小程序开发笔记【一】,查询用户参与活动列表 left join on的用法
    mysql数据插入前判断是否存在
    微信公众号通过图片选取接口上传到阿里oss
  • 原文地址:https://www.cnblogs.com/asuldb/p/10449890.html
Copyright © 2011-2022 走看看