Description


有一棵n个节点的树和一个长度为m的字符串S,树上每个节点有一个字符。问对于任意的有序数对(x,y),从x到y路径组成的字符串在S中出现次数的和。
n,m≤5⋅104n,m\le5\cdot10^4n,m≤5⋅104

Solution


很显然要在SAM上跑,我们要求的是所有路径组成的串在SAM上size的和,那么一个显然的响法就是我们点分治然后合并跨越不同子树的路径

记当前分治重心为rt,那么我们要求的就是若干条(x,rt)和(rt,y)这样路径的并组成的串在SAM上的size
这个可以正反建两个SAM,然后用SAM求出后缀树,那么所有(x,rt)都可以看作是c[rt]加上若干个前缀字符得到的新串,并且恰好就是它后缀树上到根的路径,那么我们对每个节点打出现次数+1的标记然后O(m)暴力下传就可以做到O(nlogn+nm)的复杂度了

我们发现这样做还是不行,考虑平衡规划一下。一个暴力就是我们枚举一条路径的起点,然后暴力dfs并顺便在SAM上求size。这样直接做是O(n^2)的。对于>n>\sqrt n>n​的子树我们用点分治搞,其余的子树我们直接上这个暴力。可以证明一棵树大小超过根号的子树不超过根号个,小于根号的我们一遇到就退掉也只会遍历根号个,那么总的复杂度就是十分科学的了

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
#define drp(i,st,ed) for (register int i=st;i>=ed;--i)
#define copy(x,t) memcpy(x,t,sizeof(x))typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=100005;struct edge {int y,next;} e[N*2];int ls[N],v[N],edCnt,rt,sum;
int size[N],mx[N],n,m,B;
char c[N],str[N]; LL ans;bool del[N];struct SAM {int s[N],id[N],a[N],b[N],rig[N],size[N],tot,last;int rec[N][26],son[N][26],tag[N],fa[N],mx[N];int extend(int ch,int pos) {int p,q,np,nq;p=last; np=last=++tot; mx[np]=mx[p]+1;rig[np]=pos; size[np]++;for (;p&&!rec[p][ch];p=fa[p]) rec[p][ch]=np;if (!p) fa[np]=1;else {q=rec[p][ch];if (mx[q]==mx[p]+1) fa[np]=q;else {nq=++tot; mx[nq]=mx[p]+1;copy(rec[nq],rec[q]);fa[nq]=fa[q];fa[np]=fa[q]=nq;for (;p&&rec[p][ch]==q;p=fa[p]) rec[p][ch]=nq;}}return last;}void build() {tot=last=1;rep(i,1,m) id[i]=extend(s[i],i);rep(i,1,tot) b[mx[i]]++;rep(i,1,tot) b[i]+=b[i-1];drp(i,tot,1) a[b[mx[i]]--]=i;drp(i,tot,2) {int x=a[i];if (!rig[fa[x]]) rig[fa[x]]=rig[x];son[fa[x]][s[rig[x]-mx[fa[x]]]]=x;size[fa[x]]+=size[x];}}void col(int x,int fa,int now,int len) {if (len==mx[now]) now=son[now][c[x]];else if (s[rig[now]-len]!=c[x]) now=0;if (!now) return ;tag[now]++;for (int i=ls[x];i;i=e[i].next) {if (e[i].y==fa||del[e[i].y]) continue;col(e[i].y,x,now,len+1);}}void push() {rep(i,1,tot) tag[a[i]]+=tag[fa[a[i]]];}void clear() {rep(i,1,tot) tag[i]=0;}
} SAM1,SAM2;void add_edge(int x,int y) {e[++edCnt]=(edge) {y,ls[x]}; ls[x]=edCnt;e[++edCnt]=(edge) {x,ls[y]}; ls[y]=edCnt;
}void get_size(int x,int fa) {size[x]=1; mx[x]=0;for (int i=ls[x];i;i=e[i].next) {if (e[i].y==fa||del[e[i].y]) continue;get_size(e[i].y,x); size[x]+=size[e[i].y];mx[x]=std:: max(mx[x],size[e[i].y]);}mx[x]=std:: max(mx[x],sum-size[x]);if (mx[x]<mx[rt]) rt=x;
}void dfs(int x,int fa,int now) {now=SAM1.rec[now][c[x]];if (!now) return ;ans+=SAM1.size[now];for (int i=ls[x];i;i=e[i].next) {if (e[i].y==fa||del[e[i].y]) continue;dfs(e[i].y,x,now);}
}void get(int x,int fa) {v[++v[0]]=x;for (int i=ls[x];i;i=e[i].next) {if (e[i].y==fa||del[e[i].y]) continue;get(e[i].y,x);}
}void change(int x,int fa,int xs) {SAM1.clear(); SAM2.clear();if (fa) {SAM1.col(x,fa,SAM1.son[1][c[fa]],1);SAM2.col(x,fa,SAM2.son[1][c[fa]],1);} else {SAM1.col(x,0,1,0);SAM2.col(x,0,1,0);}SAM1.push();SAM2.push();rep(i,1,m) ans+=SAM1.tag[SAM1.id[i]]*SAM2.tag[SAM2.id[m-i+1]]*xs;
}void solve(int x) {if (size[x]<=B) {v[0]=0; get(x,0);rep(i,1,v[0]) dfs(v[i],0,1);return ;}del[x]=1;change(x,0,1);for (int i=ls[x];i;i=e[i].next) {if (del[e[i].y]) continue;change(e[i].y,x,-1);}for (int i=ls[x];i;i=e[i].next) {if (del[e[i].y]) continue;sum=size[e[i].y]; rt=0;get_size(e[i].y,x);solve(rt);}
}int main(void) {scanf("%d%d",&n,&m); B=sqrt(n);rep(i,2,n) {int x,y; scanf("%d%d",&x,&y);add_edge(x,y);}scanf("%s",c+1);rep(i,1,n) c[i]-='a';scanf("%s",str+1);rep(i,1,m) SAM1.s[i]=str[i]-'a',SAM2.s[m-i+1]=str[i]-'a';SAM1.build(),SAM2.build();mx[rt=0]=INF; sum=n;get_size(1,0);solve(rt);printf("%lld\n", ans);return 0;
}

bzoj1921 [CTSC2010]珠宝商 SAM+后缀树+点分治相关推荐

  1. [CTSC2010]珠宝商 SAM+后缀树+点分治

    [CTSC2010]珠宝商 不错的题目 看似无法做,n<=5e4,8s,根号算法? 暴力一: n^2,+SAM上找匹配点的right集合sz,失配了直接退出 暴力二: O(m) 统计过lca=x ...

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

    传送门 这题思路太清奇了--->题解 1 //minamoto 2 #include<iostream> 3 #include<cstdio> 4 #include< ...

  3. BZOJ1921: [Ctsc2010]珠宝商(点分治+SAM)

    传送门 题解: 点分治,如果点数≥n\ge \sqrt{n}≥n​,则结合后缀树O(sze+m)O(sze+m)O(sze+m)处理出每个位置的开始,结束点并统计答案.否则从每个点开始暴力扩展路径. ...

  4. [Ctsc2010]珠宝商 SAM+点分治+根号分治

    Description 给定一个n个点的树,树上每个点有一个字符,再给一个长度为m的串. 两点的价值为:两点连接形成的字符串再m串中出现的次数. 询问两两点价值的和. Sample Input 3 5 ...

  5. [BZOJ1921] [CTSC2010]珠宝商

    Description Input 第一行包含两个整数 N,M,表示城市个数及特征项链的长度. 接下来的N-1 行, 每行两个整数 x,y, 表示城市 x 与城市 y 有直接道路相连.城市由1~N进行 ...

  6. bzoj1921: [Ctsc2010]珠宝商

    暴毙选手又被zo老师D费了 暴力是n^2的都会 有另一个点分的做法,就是看成两条链,然后在后缀树上跑,找到对应原串位置拼起来,是n*(logn+m) 然后就根号分治,树的大小超过阈值就点分,小于就暴力 ...

  7. bzoj1921 CTSC2010 jewelry

    大概是四节课写代码,三节课debug,七个长相差不多的dfs穿插其中-- 好了不吐槽了: 题意: 给定一棵点上有字母的树,和一个母串 求树上点两两之间 N2N^2 条路径在母串中匹配次数和 这里采用的 ...

  8. [CTSC2010]珠宝商(点分治+根号分治+后缀自动机)

    [CTSC2010]珠宝商 洛谷题目传送门 简要题意 给定一颗nnn个节点的树,和一个长度为mmm的模式串SSS 树上每个节点都有一个字符 求树上所有路径的点的字符拼成的字符串在SSS中的出现次数之和 ...

  9. 【CTSC2010】珠宝商(SAM)(点分治)(根号分治)

    传送门 给你一棵树,树上每个点有字符,询问所有树上路径形成的字符串在给定模式串中一共出现了几次.在模式串中不同位置出现要多次计算. 题解: 好题啊. 树上路径统计类的问题显然考虑的一般就是链分治和点分 ...

最新文章

  1. NLP-基础知识-001
  2. python字符串注释_python字符串注释_Python学习笔记-字符串与注释
  3. python实现弹幕_python实现b站直播自动发送弹幕功能
  4. 关于SWT/JFace中其他常用的事件
  5. mysql数据排序指令_MySQL 排序 | 菜鸟教程
  6. 微信开源推理加速工具 TurboTransformers,性能超越 PyTorch/TensorFlow 与主流优化引擎
  7. ckEditor使用JS代码调用的方法
  8. 27muduo_net库源码分析(三)
  9. 大功率UWB模块 XZM3000 移植手册 《从DWM1000到XZM3000移植手册》
  10. FS_FT_DFT_DFS_DTFT傅里叶
  11. silk lobe资源公众号_【好设计资源库】公众号素材获取方式说明
  12. Hugepages详解
  13. 计算机键入命令,win7系统安装软件出现命令行选项语法错误键入“ 命令/?”怎么办...
  14. 【c++】CTGU2022春校赛原题详解:出题人的大航海BFS
  15. linux使用windows无线网卡,linux下安装windows xp无线网卡驱动
  16. react项目里使用public的文件
  17. ACS 2017中国汽车CIO峰会10月强势登陆上海
  18. 深入浅出英语语法 -- 动词时态
  19. 基于惠斯顿电桥的压力传感器的解决方案
  20. 操作系统-------OS概述

热门文章

  1. TypeError Class constructor ExampleService cannot be invoked withou ‘new‘ at
  2. UDP protocol--udp_rcv
  3. MQTT服务器Mosquitto的使用及配置过程中的一些问题和解决方法
  4. oracle根据关键字搜索存储过程
  5. windows10系统盘瘦身
  6. 10岁小表妹也能“吃透”Geth 客户端 !360秒,快速部署 ICO Token
  7. MySQL 数据库删除操作中的 delete、drop、 truncate 区别在哪?
  8. css的侧重点(2021.7.11)
  9. 【Unity】Jay 开发日志(一)——人物的移动、死亡与平台跳跃
  10. Windows搭建幼麟麻将运行环境