此题不提供链接

题目描述

前言

好久没用SAM了。我记得上次用SAM做题还是在上次。

题解

每一种长度的总方案是确定的,所以我们只需要求出赢的方案数、平局方案数即可。

做法其实和官方正解区别不大,官方正解用的是后缀数组:

大概就是按字典序枚举后缀,然后用矩阵+线段树维护每一种长度的后缀数和算入答案的贡献。

这样太麻烦了。请读者们记住一点:所有可以用SA做的题都可以用SAM做,而且只会更简单。

我们倒着插入字符建立后缀自动机。如果把后缀自动机的 p a r e n t t r e e \rm parent\,tree parenttree 建出来,然后把每个节点的子节点按照往后的第一个字符排序,那么此时按DFS序遍历这棵 p a r e n t t r e e \rm parent\,tree parenttree 就可以得到按字典序排序并去重过后所有子串( p a r e n t t r e e \rm parent\,tree parenttree 上的一个节点代表的是一个 e n d p o s \rm endpos endpos 集的子串)。

叶子节点代表的最大子串就是原字符串的后缀,也就是说,它不仅实现了对后缀排序,而且同时对所有子串排序还去重,而两节点的LCA就是最长公共前缀。你还要后缀数组有何用?

于是我们就可以先用简单的树DP求出每个节点代表的子串在A中和B中出现的次数 a a a 和 b b b,然后按DFS序遍历,在线段树上对应区间矩乘,乘上如下矩阵:

( 1 0 0 0 1 1 0 0 1 ) ( 加 进 答 案 ) b × ( 1 1 0 0 1 0 0 0 1 ) ( 增 加 贡 献 ) a \begin{pmatrix}1&0&0\\0&1&1\\0&0&1\end{pmatrix}_{_{(加进答案)}}^b \times \begin{pmatrix}1&1&0\\0&1&0\\0&0&1\end{pmatrix}_{_{(增加贡献)}}^a ⎝⎛​100​010​011​⎠⎞​(加进答案)​b​×⎝⎛​100​110​001​⎠⎞​(增加贡献)​a​

同时用简单的标记把 a ⋅ b a\cdot b a⋅b(平局的情况数)记录下来,以便最后可以 O ( n ) O(n) O(n) 求出所有长度平局的方案数。

由于子串是去了重的,所以我们求出的就是赢的方案数,没有包含平局,不多不少,不需要再求输的方案数。而且这种方法相比于后缀数组的做法,只用乘两种矩阵,不需要清零。

代码

还是要用四个元素的矩阵省空间。

#include<cstdio>//JZM yyds!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define uns unsigned
#define MOD 1000000007ll
#define MAXN 200005
#define INF 1e18
#define lowbit(x) (x&(-x))
#define IF it->first
#define IS it->second
using namespace std;
inline ll read(){ll x=0;bool f=1;char s=getchar();while((s<'0'||s>'9')&&s>0)f^=(s=='-'),s=getchar();while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();return f?x:-x;
}
inline ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
int n,m,k;
ll f[MAXN],g[MAXN];
char s[MAXN<<1];
struct matrix{ll a,b,c,d;matrix(){}matrix(ll A,ll B,ll C,ll D){a=A,b=B,c=C,d=D;}inline matrix operator*(const matrix&y){return matrix(y.a+a*y.c,y.b+a*y.d+b,c*y.c,c*y.d+d);}
}t[MAXN<<2],B=matrix(1,0,1,0),C=matrix(0,0,1,1);
bool lz[MAXN<<2];//区间修改单点查询,只需维护懒标记
inline matrix jzm(matrix a,int b){//矩阵(快速)幂 matrix r=matrix(0,0,1,0);for(;b;b>>=1,a=a*a)if(b&1)r=r*a;return r;
}
inline void pushd(int x,int l,int r){if(l==r)return;if(lz[x]){if(lz[x<<1])t[x<<1]=t[x<<1]*t[x];else t[x<<1]=t[x],lz[x<<1]=1;if(lz[x<<1|1])t[x<<1|1]=t[x<<1|1]*t[x];else t[x<<1|1]=t[x],lz[x<<1|1]=1;lz[x]=0;}
}
inline void add(int x,int l,int r,int a,int b,bool d,int cd){if(a>b||a>r||b<l)return;pushd(x,l,r);if(l>=a&&r<=b){matrix cg=jzm(d?B:C,cd);if(lz[x])t[x]=t[x]*cg;else t[x]=cg,lz[x]=1;return;}int mid=(l+r)>>1;if(a<=mid)add(x<<1,l,mid,a,b,d,cd);if(b>mid)add(x<<1|1,mid+1,r,a,b,d,cd);
}
inline void cntans(int x,int l,int r){//下放标记,记录最终答案 pushd(x,l,r);if(l==r){matrix ct=matrix(0,0,0,0);if(lz[x])ct=t[x];f[l]=ct.b;return;}int mid=(l+r)>>1;cntans(x<<1,l,mid),cntans(x<<1|1,mid+1,r);
}
struct SAM{int ch[26],len,fa;
}sam[MAXN<<2];
int las=1,tot=1;
int na[MAXN<<2],nb[MAXN<<2],id[MAXN<<2];
char cp[MAXN<<2];
inline int samadd(int c){int p=las,np=las=++tot;sam[np].len=sam[p].len+1;for(;p&&!sam[p].ch[c];p=sam[p].fa)sam[p].ch[c]=np;if(!p)sam[np].fa=1;else{int q=sam[p].ch[c];if(sam[q].len==sam[p].len+1)sam[np].fa=q;else{int nq=++tot;sam[nq]=sam[q];sam[nq].len=sam[p].len+1,sam[q].fa=sam[np].fa=nq;for(;p&&sam[p].ch[c]==q;p=sam[p].fa)sam[p].ch[c]=nq;}}return np;
}
inline bool cmp(int x,int y){return cp[x]<cp[y];}
vector<int>G[MAXN<<2];
int la[MAXN<<1];
inline void pdfs(int x){for(uns i=0;i<G[x].size();i++){int v=G[x][i];pdfs(v);na[x]+=na[v],nb[x]+=nb[v],id[x]=id[v];}cp[x]=s[id[x]+sam[sam[x].fa].len];sort(G[x].begin(),G[x].end(),cmp);
}
inline void dfs(int x){if(x>1){int l=sam[sam[x].fa].len+1,r=sam[x].len;add(1,1,k,l,r,0,nb[x]);add(1,1,k,l,r,1,na[x]);g[l]+=1ll*na[x]*nb[x],g[r+1]-=1ll*na[x]*nb[x];}for(uns i=0;i<G[x].size();i++)dfs(G[x][i]);
}
signed main()
{freopen("game.in","r",stdin);freopen("game.out","w",stdout);scanf("%s",s+1);n=strlen(s+1);scanf("%s",s+n+1);m=strlen(s+n+1),k=min(n,m);for(int i=n;i>0;i--){la[i]=samadd(s[i]-'a');na[la[i]]++,id[la[i]]=i;}for(int i=n+m,o=1,ls=1;i>n;i--){if(o&&s[i]==s[i-m])la[i]=la[i-m],ls=la[i];else o=0,las=ls,la[i]=samadd(s[i]-'a'),ls=la[i];nb[la[i]]++,id[la[i]]=i;}for(int i=2;i<=tot;i++)G[sam[i].fa].push_back(i);pdfs(1),dfs(1),cntans(1,1,k);ll tg=0;for(int i=1;i<=k;i++){tg+=g[i];ll s=(n-i+1ll)*(m-i+1ll);ll a=f[i],b=tg,c=s-a-b;ll d=gcd(s,a),e=gcd(s,b),f=gcd(s,c);printf("%lld/%lld %lld/%lld %lld/%lld\n",a/d,s/d,b/e,s/e,c/f,s/f);}return 0;
}

update minutes later

看到了不用矩阵的更快的SA做法,感觉自己被打脸得好快啊。

其实我没有说错,SAM的做法确实更简单暴力。SAM和SA都是比较万能的字符串算法,一个偏向于树形结构,一个偏向于序列,按个人习惯哪种分析起来顺手就用哪个吧。

多校冲刺NOIP模拟6 - 游戏——矩阵乘法、后缀自动机SAM相关推荐

  1. P1397 [NOI2013] 矩阵游戏(矩阵乘法欧拉定理)

    P1397 [NOI2013] 矩阵游戏(矩阵乘法&欧拉定理) #include<bits/stdc++.h> using namespace std; typedef long ...

  2. 【BZOJ4861】[Beijing2017]魔法咒语 矩阵乘法+AC自动机+DP

    [BZOJ4861][Beijing2017]魔法咒语 题意:别看BZ的题面了,去看LOJ的题面吧~ 题解:显然,数据范围明显的分成了两部分:一个是L很小,每个基本词汇长度未知:一个是L很大,每个基本 ...

  3. [2021.4.5多校省选模拟30]最小表示——map建边+广义SAM

    前言:虽然这题前面加了个括号是"省选模拟30",但是在accoders上是比赛"省选模拟31"里面的. 题目描述 题解 先贴出官方正解,是用的和后缀数组: 根据 ...

  4. 【NOIP 模拟赛】平均数 涂色游戏 序列题解

    吐槽(完跪):神tmNOIP模拟赛,看到大题头根本高级数据结构什么的想都没想,结果死的很惨,第三题竟然要可持久化线段树,这NOIP我服了. 平均数: 这道题40分相信大家都能拿到,剩下的60分讲真考验 ...

  5. NOIP模拟赛 四校联考 递推 + 分类讨论 + 树上期望

    NOIP 模拟题 题目名称兔子被子蚊子 源程序文件名rabbit.cpp quilt.cpp mosquito.cpp 输入文件名rabbit.in quilt.in mosquito.in 输出文件 ...

  6. 2019.08.20【NOIP提高组】模拟 B 组 排序、DP+递推、矩阵乘法+数位DP/类欧

    文章目录 0 旅游(travel) 1 做梦(dream) 2 数数(count) 初见时,她说,"我无力自保,无处可去,无人可依." 多年后,他说,"我教你射箭,你已有 ...

  7. 闵梓轩大佬のnoip模拟题D1 总结 2017/10/26

    背景 题目概括 T1 题面 分析 90分算法 满分算法 T2 题面 分析 部分分算法 满分算法 满分代码 T3 题面 分析 代码 总结 背景 这道题目是去年的金牌大佬闵梓轩在一年前出的一套noip模拟 ...

  8. 冲刺NOIP复习,算法知识点总结

    前言        离NOIP还有一个星期,匆忙的把整理的算法补充完善,看着当时的整理觉得那时还年少.第二页贴了几张从贴吧里找来的图片,看着就很热血的.当年来学这个竞赛就是为了兴趣,感受计算机之美的. ...

  9. NOI.AC NOIP模拟赛 第六场 游记

    NOI.AC NOIP模拟赛 第六场 游记 queen 题目大意: 在一个\(n\times n(n\le10^5)\)的棋盘上,放有\(m(m\le10^5)\)个皇后,其中每一个皇后都可以向上.下 ...

最新文章

  1. 指定动态分区_重点解析!2021湖南中级职称评审动态趋势
  2. API for org.eclipse.paho.client.mqttv3
  3. django view
  4. Leetcode题库191.位1的个数(C实现)
  5. Windows 10 关闭Hyper-V
  6. 面向.NET开发人员的Dapr——分布式世界
  7. Java BigDecimal floatValue()方法与示例
  8. [Classic] 日文版《出师表》
  9. html 新浪博客,html
  10. python tclerror_Python Tk _tkinter.TclError:无效的命令名称“.42818376”
  11. Java中的String.hashCode()方法可能有问题?
  12. ASP.NET MVC4 使用UEditor富文本
  13. Linux服务器上安装node.js
  14. 使用ACR及MIUI自带通话录音实现通话录音读取及上传
  15. 如何应对硬盘无法识别通电异响等那些七七八八的物理故障
  16. 盖亚绘制的星图里,藏着银河系不为人知的混乱过去
  17. 西安三本计算机专业可报院校,西安三本大学前十名, 西北大学现代学院仅第四...
  18. 《笨办法学python3》再笨的人都能学会python,附PDF,拿走不谢
  19. esp01s+blinker WOL远程开机
  20. ERROR 1406 (22001): Data Too Long, field len 30, data len 48

热门文章

  1. 夜深人静写算法(三十二)- 费马小定理
  2. 每日一词20190316——GIS矢量数据和栅格数据对比
  3. 矩阵分解 (乘法篇)
  4. 抖音小店无货源玩法分享(五)抖店类目该如何选择
  5. 小程序uni-app介绍
  6. python def -> : ->什么意思
  7. 第九层(1):初识STL
  8. oracle忘记密码找回
  9. 安全日志:/var/log/secure 详解
  10. 出版图书二维码,要如何制作呢?