题意

给定一个长度为n字符串,字符集大小为m(1<=n,m<=1e6),求\(\bigoplus_{c = 1}^{m}\left(h(c) \cdot 3^c \bmod (10^9+7)\right)\)的值。其中h(c)为将c加到字符串末尾产生的新的本质不同的子串数目。

解题思路

比赛的时候没做出来,颁奖的时候听lts和lsx讲了之后发现可以用SAM做,而且板子稍微改改就可以了。

具体就是每次添加一个字符最多新建2个节点,根据SAM的性质,添加c后新建节点对本质不同的子串的数目的贡献就是h(c),用len[i]-len[link[i]]就能算出来。

现场赛的时候我一直没想到添加结点之后要怎么把这个结点再删掉,没有立刻想出来,然后就去做D数位DP了,但是其实很简单,可以把extend函数改一改,对于询问只算答案不修改原来的节点,这样就不需要删除;或者用一个容器记录修改了哪一些结点,询问完了再遍历一遍改回去。这两种方法应该都是\(O(1)\)修改的。

代码实现

和叉姐的标程对拍没有出错,应该是对的吧

#include<bits/stdc++.h>
using namespace std; typedef long long ll;
const int maxn=1e6+5;
const int mod=1e9+7; struct Suffix_Automaton{
struct state{
int len,link;
map<int,int>next;
}st[maxn<<1];
int last,tot; int sz; void init(){
st[1].len=0;st[0].link=0;
st[1].next.clear();
last=tot=1;;
}
int newnode(){
++tot;
st[tot].len=st[tot].link=0;
st[tot].next.clear();
return tot;
}
void extend(int c){
int p=last;
int cur=newnode();
st[cur].len=st[last].len+1;
last=cur;
while(p && !st[p].next.count(c)){
st[p].next[c]=cur;
p=st[p].link;
}
if(!p)st[cur].link=1;
else{
int q=st[p].next[c];
if(st[p].len+1==st[q].len)st[cur].link=q;
else{
int clone=newnode();
st[clone].len=st[p].len+1;
st[clone].next=st[q].next;
st[clone].link=st[q].link;
st[q].link=st[cur].link=clone;
while(st[p].next[c]==q){
st[p].next[c]=clone;
p=st[p].link;
}
}
}
}
ll solve(int c){
ll res=0; int p=last;
int cur=newnode();
st[cur].len=st[last].len+1;
//last=cur;
while(p && !st[p].next.count(c)){
//st[p].next[c]=cur;
p=st[p].link;
}
if(!p){
st[cur].link=1;
res=st[cur].len-st[st[cur].link].len;
}
else{
int q=st[p].next[c];
if(st[p].len+1==st[q].len){
st[cur].link=q;
res=st[cur].len-st[st[cur].link].len;
}
else{
int clone=newnode();
st[clone].len=st[p].len+1;
//st[clone].next=st[q].next;
st[clone].link=st[q].link;
//while(p && st[p].next[c]==q){
//st[p].next[c]=clone;
//p=st[p].link;
//}
//st[q].link=clone;
st[cur].link=clone; //结点q的link变为clone,所以需要把原来的删掉再把新的加进去
res=((res-(st[q].len-st[st[q].link].len))%mod+mod)%mod;
res=(res+st[q].len-st[clone].len)%mod; res=(res+st[cur].len-st[st[cur].link].len)%mod;
res=(res+st[clone].len-st[st[clone].link].len)%mod;
}
} tot=sz;
return (res%mod+mod)%mod;
}
}S;
int n,m;
int main()
{
while(~scanf("%d %d",&n,&m)){
S.init();
int x;
for(int i=1;i<=n;i++){
scanf("%d",&x);
S.extend(x);
}
S.sz=S.tot;
ll ans=0,three=1,hc;
for(int i=1;i<=m;i++){
three=three*3%mod;
hc=S.solve(i);
ans^=(hc*three%mod);
}
printf("%lld\n",ans);
}
return 0;
}

2019HNCPC C Distinct Substrings 后缀自动机的更多相关文章

  1. 【CF316G3】Good Substrings 后缀自动机

    [CF316G3]Good Substrings 题意:给出n个限制(p,l,r),我们称一个字符串满足一个限制当且仅当这个字符串在p中的出现次数在[l,r]之间.现在想问你S的所有本质不同的子串中, ...

  2. SPOJ - SUBST1 New Distinct Substrings —— 后缀数组 单个字符串的子串个数

    题目链接:https://vjudge.net/problem/SPOJ-SUBST1 SUBST1 - New Distinct Substrings #suffix-array-8 Given a ...

  3. SPOJ - DISUBSTR Distinct Substrings (后缀数组)

    Given a string, we need to find the total number of its distinct substrings. Input T- number of test ...

  4. 【SPOJ – SUBST1】New Distinct Substrings 后缀数组

    New Distinct Substrings 题意 给出T个字符串,问每个字符串有多少个不同的子串. 思路 字符串所有子串,可以看做由所有后缀的前缀组成. 按照后缀排序,遍历后缀,每次新增的前缀就是 ...

  5. SPOJ NSUBSTR Substrings 后缀自动机

    人生第一道后缀自动机,总是值得纪念的嘛.. 后缀自动机学了很久很久,先是看CJL的论文,看懂了很多概念,关于right集,关于pre,关于自动机的术语,关于为什么它是线性的结点,线性的连边.许多铺垫的 ...

  6. ●SPOJ 8222 NSUBSTR–Substrings(后缀自动机)

    题链: http://www.spoj.com/problems/NSUBSTR/ 题解: 后缀自动机的水好深啊!懂不了相关证明,带着结论把这个题做了.看来这滩深水要以后再来了. 本题要用到一个叫 R ...

  7. SPOJ8222 NSUBSTR - Substrings(后缀自动机)

    You are given a string S which consists of 250000 lowercase latin letters at most. We define F(x) as ...

  8. SPOJ NSUBSTR Substrings ——后缀自动机

    建后缀自动机 然后统计次数,只需要算出right集合的大小即可, 然后更新f[l[i]]和rit[i]取个max 然后根据rit集合短的一定包含长的的性质,从后往前更新一遍即可 #include &l ...

  9. SPOJ DISUBSTR Distinct Substrings 后缀数组

    题意:统计母串中包含多少不同的子串 然后这是09年论文<后缀数组——处理字符串的有力工具>中有介绍 公式如下: 原理就是加上新的,减去重的,这题是因为打多校才补的,只能说我是个垃圾 #in ...

随机推荐

  1. C++ Json工具--Jsoncpp用法简介

    文章目录 Json简介 用法简介 数据类型 C++代码示例 代码执行输出结果 JSON在线解析及格式化验证 - JSON.cn Json简介 JSON(JavaScript Object Notati ...

  2. 微信号可以改了?我用 Python 发现了隐藏的 6 大秘密.

    “听说,微信可以改微信号了! ” 不知道谁扯了一嗓子,让办公室变成了欢乐的海洋 张姐流下了激动的泪水:“太好了!姐的年龄终于不用暴露在微信号了!” 很多人学习python,不知道从何学起.很多人学习p ...

  3. Qt之先用了再说系列-多线程方式2

    继Qt之先用了再说系列-多线程方式2 本次说说在Qt里多线程使用第2种方式,也是qt官方比较推荐用的方式,相对与直接继承QThread 重写run方法中灵活了一些,如果第一次使用可能会转不湾来,没有直 ...

  4. python中1 is True 的结果为False,is判断与==判断的区别

    python中1 is True 的结果为False,而1 == True的结果为True. python中True的数值就是1,那为什么1 is True 的结果为False呢? 因为is判断和== ...

  5. C#开发笔记之02-什么时候使用OnXXX方法,这种命名是什么意思?

    C#开发笔记概述 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/958 访问. 你也许经常会看到别人写的代码里有OnXX ...

  6. Css 图片自适应

    设置 CSS .container{ overflow:auto; } img{ width:100%; height:auto; overflow:hidden; } 设置 template < ...

  7. 面经手册 · 第4篇《HashMap数据插入、查找、删除、遍历,源码分析》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 在上一章节我们讲解并用数据验证了,HashMap中的,散列表的实现.扰动函数.负载因 ...

  8. 多次调用Promise的then会返回什么?

    //做饭 function cook(){ console.log('开始做饭.'); var p = new Promise(function(resolve, reject){ //做一些异步操作 ...

  9. 关于Exceptionless日志收集框架会被Fiddler抓包,从而获取到ApiKey的问题

    关于Exceptionless日志收集框架会被Fiddler抓包,从而获取到ApiKey的问题 环境:Exceptionless5.0.0.Linux.WPF客户端.Fiddler 问题:在使用Exc ...

  10. python使用mongodb--基础操作(增、删、改、查)

    连接数据库 client = pymongo.MongoClient(host="127.0.0.1", port=6666) db = client['Media'] db.au ...