[CTSC2010]珠宝商

不错的题目

看似无法做,n<=5e4,8s,根号算法?

暴力一:

n^2,+SAM上找匹配点的right集合sz,失配了直接退出

暴力二:

O(m)

统计过lca=x的路径,

没法直接合并,就间接合并!

把所有形如(z,x)(x,y)的路径在原串所有出现位置打上标记

原串每个点维护结束路径(zi,x)和开始路径(x,yi)个数(实际上只有char=a[x]的位置才有值),然后乘起来就是贡献

打标记:

SAM匹配

麻烦事是:这个是往某个字符串前面加字符,但是要在字符串末尾位置打上标记

(和通常SAM末尾+字符,末尾位置打标记不同)

要用到后缀树、前缀树

(z,x)路径是前缀树

(x,y)路径是后缀树(反串前缀树)

把parent树建成前缀树,走一下。

建树:

(第一次建后缀树)

1.每个节点随便记录一个parent树子树里的后缀出现位置pos[x]

2.预处理parent树每个边第一个字符(最多26条边),

3.可能在边上,所以状态用(p,l)记录:下面是p点,在p点上方l处。

trans,根据l大小进行讨论

打标记:

trans打上tag

最后直接下放

结合

设阈值B

size<=B用暴力一,不递归了,O(B^2*n/B)

size>B用暴力二,分治树节点个数不超O(B)最多进行O(Bm)

B取sqrt(n)即可

容斥?

暴力二离线处理共线,显然会重复。分治到下一层的时候为上一层去重。

也是根据size选择去重方法

Code

  1. // luogu-judger-enable-o2
  2. #include<bits/stdc++.h>
  3. #define reg register int
  4. #define il inline
  5. #define fi first
  6. #define se second
  7. #define mk(a,b) make_pair(a,b)
  8. #define numb (ch^'0')
  9. using namespace std;
  10. typedef long long ll;
  11. template<class T>il void rd(T &x){
  12. char ch;x=;bool fl=false;
  13. while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
  14. for(x=numb;isdigit(ch=getchar());x=x*+numb);
  15. (fl==true)&&(x=-x);
  16. }
  17. template<class T>il void output(T x){if(x/)output(x/);putchar(x%+'');}
  18. template<class T>il void ot(T x){if(x<) putchar('-'),x=-x;output(x);putchar(' ');}
  19. template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}
  20.  
  21. namespace Miracle{
  22. const int N=;
  23. int B;
  24. char s[N];
  25. int n,m;
  26. char a[N];
  27. struct node{
  28. int nxt,to;
  29. }e[*N];
  30. int hd[N],cnt;
  31. void add(int x,int y){
  32. e[++cnt].nxt=hd[x];
  33. e[cnt].to=y;
  34. hd[x]=cnt;
  35. }
  36. ll ans;
  37. struct SAM{
  38. int ch[*N][],cnt,len[*N],fa[*N];
  39. int son[*N][],nd;
  40. int tag[*N],exi[*N],ad[*N],pos[*N];
  41. int sz[*N];
  42. char s[*N];
  43. SAM(){
  44. cnt=nd=;
  45. }
  46. void ins(int c,int l){
  47. int p=nd;len[nd=++cnt]=l;
  48. exi[cnt]=l;sz[cnt]=;
  49. pos[cnt]=l;
  50.  
  51. for(;p&&ch[p][c]==;p=fa[p]) ch[p][c]=nd;
  52. if(!p){
  53. fa[nd]=;return;
  54. }
  55. int q=ch[p][c];
  56. if(len[q]==len[p]+){
  57. fa[nd]=q;return;
  58. }
  59. len[++cnt]=len[p]+;
  60. fa[cnt]=fa[q];fa[q]=fa[nd]=cnt;
  61. for(reg i=;i<;++i) ch[cnt][i]=ch[q][i];
  62. for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=cnt;
  63. }
  64. struct edge{
  65. int nxt,to;
  66. }e[*N];
  67. int hd[*N],tot;
  68. void add(int x,int y){
  69. e[++tot].nxt=hd[x];
  70. e[tot].to=y;hd[x]=tot;
  71. }
  72. void dfs(int x){//pushup sz!!!
  73. for(reg i=hd[x];i;i=e[i].nxt){
  74. int y=e[i].to;
  75. dfs(y);
  76. sz[x]+=sz[y];
  77. if(!pos[x]) pos[x]=pos[y];
  78. }
  79. }
  80. void pre(){
  81. // cout<<s+1<<endl;
  82. for(reg i=;i<=m;++i) ins(s[i]-'a',i);
  83. for(reg i=;i<=cnt;++i) add(fa[i],i);
  84. dfs();
  85. }
  86. void build(){
  87. for(reg i=;i<=cnt;++i){
  88. son[fa[i]][s[pos[i]-len[fa[i]]]-'a']=i;
  89. }
  90. }
  91. // int jump(int p,int c){//return to cur ;
  92. // return ch[p][c];
  93. // }
  94. void trans(pair<int,int>&st,int c,int ok){//and add tag
  95. if(st.fi==) return;
  96. if(st.se==){//to son
  97. st.fi=son[st.fi][c];
  98. st.se=len[st.fi]-len[fa[st.fi]];
  99. }else{
  100. int to=pos[st.fi]-len[st.fi]+st.se-;
  101. if(s[to]-'a'==c){
  102. st.se--;
  103. }else st.fi=;
  104. }
  105. if(st.fi!=&&ok){
  106. ++tag[st.fi];
  107. }
  108. return;
  109. }
  110. void pushdown(int x){
  111. for(reg i=hd[x];i;i=e[i].nxt){
  112. int y=e[i].to;
  113. tag[y]+=tag[x];
  114. pushdown(y);
  115. }
  116. }
  117. void calc(){
  118. pushdown();
  119. // cout<<" cnt "<<cnt<<endl;
  120. // prt(tag,1,cnt);
  121. for(reg i=;i<=cnt;++i){
  122. if(exi[i]) ad[exi[i]]+=tag[i];
  123. }
  124. // prt(ad,1,m);
  125. }
  126. void clear(){
  127. memset(tag,,sizeof tag);
  128. memset(ad,,sizeof ad);
  129. }
  130. }sam1,sam2;//sam1:pre tree ///// sam2:suf tree
  131. bool vis[N];
  132. int sz[N],nowsz;
  133. int rt;
  134. void dfs3(int x,int fa,int p,int w){//sz < B // w=1/-1
  135. p=sam1.ch[p][a[x]-'a'];
  136. if(p){
  137. ans+=(ll)w*sam1.sz[p];
  138. }
  139. else return ;//warinnig!!
  140. for(reg i=hd[x];i;i=e[i].nxt){
  141. int y=e[i].to;
  142. if(y==fa||vis[y]) continue;
  143. dfs3(y,x,p,w);
  144. }
  145. }
  146. int sta[N],top;
  147. void dfs2(int x,int fa){//qu chong
  148. sta[++top]=x;
  149. int p=;
  150. for(reg i=top;i>=;--i) p=sam1.ch[p][a[sta[i]]-'a'];
  151. dfs3(sta[],sta[],p,-);
  152. for(reg i=hd[x];i;i=e[i].nxt){
  153. int y=e[i].to;
  154. if(vis[y]||y==fa) continue;
  155. dfs2(y,x);
  156. }
  157. sta[top--]=;
  158. }
  159. void dfs4(int x,int fa){
  160. dfs3(x,,,);
  161. for(reg i=hd[x];i;i=e[i].nxt){
  162. int y=e[i].to;
  163. if(y==fa||vis[y]) continue;
  164. dfs4(y,x);
  165. }
  166. }
  167.  
  168. void fin(int x,int fa){
  169. sz[x]=;
  170. int mxsz=;
  171. for(reg i=hd[x];i;i=e[i].nxt){
  172. int y=e[i].to;if(vis[y]||y==fa) continue;
  173. fin(y,x);
  174. sz[x]+=sz[y];
  175. mxsz=max(mxsz,sz[y]);
  176. }
  177. mxsz=max(mxsz,nowsz-sz[x]);
  178. if(mxsz<=nowsz/){
  179. rt=x;
  180. }
  181. }
  182. void dfs1(int x,int fa,pair<int,int>p1,pair<int,int>p2){
  183. sz[x]=;
  184. sam1.trans(p1,a[x]-'a',);
  185. sam2.trans(p2,a[x]-'a',);
  186. for(reg i=hd[x];i;i=e[i].nxt){
  187. int y=e[i].to;
  188. if(vis[y]||y==fa) continue;
  189. dfs1(y,x,p1,p2);
  190. sz[x]+=sz[y];
  191. }
  192. }
  193.  
  194. void divi(int x,int fa){
  195. // cout<<" divi "<<x<<" fa "<<fa<<" ans "<<ans<<endl;
  196. if(nowsz<=B){
  197. if(fa){
  198. top=;sta[++top]=fa;
  199. dfs2(x,);
  200. }
  201. dfs4(x,);
  202. }else{
  203. if(fa){
  204. sam1.clear();sam2.clear();
  205. pair<int,int>p1=mk(,),p2=mk(,);
  206. sam1.trans(p1,a[fa]-'a',);
  207. sam2.trans(p2,a[fa]-'a',);
  208. dfs1(x,,p1,p2);
  209. //cout<<" a[fa] "<<a[fa]<<endl;
  210. sam1.calc();
  211. sam2.calc();
  212.  
  213. for(reg i=;i<=m;++i){
  214. ans-=(ll)sam1.ad[i]*sam2.ad[m-i+];
  215. //cout<<" ii "<<i<<" "<<sam1.ad[i]<<" and "<<sam2.ad[m-i+1]<<endl;
  216. }
  217. }
  218. // cout<<" after ans "<<ans<<endl;
  219. // if(fa){
  220. // top=0;sta[++top]=fa;
  221. // dfs2(x,0);
  222. // }
  223.  
  224. rt=;fin(x,);
  225. sam1.clear();sam2.clear();
  226. dfs1(rt,,mk(,),mk(,));//warning!! start from sam's rt
  227. sam1.calc();
  228. sam2.calc();
  229. for(reg i=;i<=m;++i){
  230. //if(s[i]==a[rt])
  231. ans+=(ll)sam1.ad[i]*sam2.ad[m-i+];
  232. }
  233.  
  234. vis[rt]=;
  235. int tmp=rt;
  236. for(reg i=hd[rt];i;i=e[i].nxt){
  237. int y=e[i].to;
  238. if(!vis[y]) {
  239. nowsz=sz[y];
  240. divi(y,tmp);
  241. }
  242. }
  243. }
  244. }
  245. int main(){
  246. rd(n);rd(m);
  247. B=sqrt(n);
  248. int x,y;
  249. for(reg i=;i<n;++i){
  250. rd(x);rd(y);add(x,y);add(y,x);
  251. }
  252. scanf("%s",s+);
  253. for(reg i=;i<=n;++i){
  254. a[i]=s[i];
  255. }
  256. scanf("%s",sam1.s+);
  257. memcpy(sam2.s,sam1.s,sizeof sam1.s);
  258. reverse(sam2.s+,sam2.s+m+);
  259. sam1.pre();sam1.build();
  260. sam2.pre();sam2.build();
  261.  
  262. nowsz=n;
  263. divi(,);
  264. cout<<ans;
  265. return ;
  266. }
  267.  
  268. }
  269. signed main(){
  270. Miracle::main();
  271. return ;
  272. }
  273.  
  274. /*
  275. Author: *Miracle*
  276. Date: 2019/4/9 17:06:25
  277. */

总结

1.根号讨论暴力结合

2.点分治合并是难点,

要不然数据结构维护决策位置(树形Dp思路)

要不然都把上下路径都求出来放在一起,再人工拼凑(容斥思路)

3.有了前缀后缀树,妈妈再也不用担心一个串往前插入字符怎么匹配了!

[CTSC2010]珠宝商 SAM+后缀树+点分治的更多相关文章

  1. 洛谷P4218 [CTSC2010]珠宝商(后缀自动机+点分治)

    传送门 这题思路太清奇了……->题解 //minamoto #include<iostream> #include<cstdio> #include<cstring ...

  2. 关于广义后缀树(多串SAM)的总结

    之前我们给的SAM的例题,基本上是一个串建SAM的就能做的 如果要建多个串的SAM应该怎么做呢 首先看题,bzoj2780 我一开始的想法是SA以前的弄法,把串拼起来,中间加分隔符做SAM 这题确实可 ...

  3. 【UOJ131/NOI2015D2T2-品酒大会】sam求后缀树

    题目链接:http://uoj.ac/problem/131 题意:给出一个字符串,第i个字符对应的值为a[i], 对于i∈[0,n),求最长公共前缀大于等于i的字串对个数,并求这些字符串对开头对应值 ...

  4. CTSC2010 珠宝商

    珠宝商 题目描述 Louis.PS 是一名精明的珠宝商,他出售的项链构造独特,很大程度上是因为他的制作方法与众不同.每次 Louis.PS 到达某个国家后,他会选择一条路径去遍历该国的城市.在到达一个 ...

  5. [模板] 后缀自动机&&后缀树

    后缀自动机 后缀自动机是一种确定性有限状态自动机, 它可以接收字符串\(s\)的所有后缀. 构造, 性质 翻译自毛子俄罗斯神仙的博客, 讲的很好 后缀自动机详解 - DZYO的博客 - CSDN博客 ...

  6. P4218 [CTSC2010]珠宝商

    P4218 [CTSC2010]珠宝商 神题... 可以想到点分治,细节不写了... (学了个新姿势,sam可以在前面加字符 但是一次点分治只能做到\(O(m)\),考虑\(\sqrt n\)点分治, ...

  7. 康复计划#1 再探后缀自动机&后缀树

    本篇口胡写给我自己这样的东西都忘光的残废选手 以及那些刚学SAM,看了其他的一些东西并且没有完全懵逼的人 (初学者还是先去看有图的教程吧,虽然我的口胡没那么好懂,但是我觉得一些细节还是讲清楚了的) 大 ...

  8. CF700E:Cool Slogans(SAM,线段树合并)

    Description 给你一个字符串,如果一个串包含两个可有交集的相同子串,那么这个串的价值就是子串的价值+1.问你给定字符串的最大价值子串的价值. Input 第一行读入字符串长度$n$,第二行是 ...

  9. luoguP5108 仰望半月的夜空 [官方?]题解 后缀数组 / 后缀树 / 后缀自动机 + 线段树 / st表 + 二分

    仰望半月的夜空 题解 可以的话,支持一下原作吧... 这道题数据很弱..... 因此各种乱搞估计都是能过的.... 算法一 暴力长度然后判断判断,复杂度\(O(n^3)\) 期望得分15分 算法二 通 ...

随机推荐

  1. CLOUD添加自定义基础数据

    1.打开bos平台,文件-新建-复制-基础资料 2.新建目标对象 3.发布 4.开始新增对象 5.明细维护,完成 6.添加成功

  2. 使用Elasticsearch 出现的拒绝连接

    pom 文件 spring: elasticsearch: jest: uris: http://192.168.124.142:9201 # data: # elasticsearch: # clu ...

  3. hive表链接

    等值连接 不等职链接 外部链接 没有包含在聚合函数(这里是count)中的列,都需要包含在group by函数中: 正确的外链接的写法,用的是右外链接: 自链接表 把同一张表 看成了2张表

  4. Golang的md5 hash计算

    Golang计算md5值的方法都是接收byte型slice([]byte).而且使用习惯上也觉得略奇怪. 看了好几个例子才看懂. 感觉Golang标准库在设计这些模块的时候,都会考虑使用带New关键字 ...

  5. linux audit审计(5)--audit规则配置

    audit可以配置规则,这个规则主要是给内核模块下发的,内核audit模块会按照这个规则获取审计信息,发送给auditd来记录日志. 规则类型可分为: 1.控制规则:控制audit系统的规则: 2.文 ...

  6. 使用Guava cache构建本地缓存

    前言 最近在一个项目中需要用到本地缓存,在网上调研后,发现谷歌的Guva提供的cache模块非常的不错.简单易上手的api:灵活强大的功能,再加上谷歌这块金字招牌,让我毫不犹豫的选择了它.仅以此博客记 ...

  7. HTTP协议 - 使用php模拟get/post请求

    首先 有个疑问, 是不是只有浏览器才能发送http 请求? 答案肯定是错的,第一篇就说了,http是由请求行,请求头,请求主体三个部分组成,那么我们可不可以用代码来模拟一下get和post请求呢: 首 ...

  8. codeforces570C

    Replacement CodeForces - 570C 话说很久很久以前,孙悟空被压在了山下,很无聊.于是他找了一个只包含小写字母和字符"." 的字符串. 由于他比较无聊,他就 ...

  9. h.264并行熵解码

    在前面讨论并行解码的章节中,我们专注于讨论解码的宏块重建部分,甚至把宏块重建描述成宏块解码,这是因为在解码工作中,宏块重建确实占了相当大的比重,不过解码还包含其它的部分,按照解码流程可粗略分为: 读取 ...

  10. nodejs eggjs框架 爬虫 readhub.me

    最近做了一款 高仿ReadHub小程序  微信小程序 canvas 自动适配 自动换行,保存图片分享到朋友圈  https://gitee.com/richard1015/News 具体代码已被开源, ...