[CTSC2010]珠宝商

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

解题思路

路径统计?一听就很点分治
字串出现次数?一听就很SAM
那这个题实际也就是这两个的结合了

I

首先有一个显然的O(n2)O(n^2)O(n2)做法
建出SAMSAMSAM
并求出每个节点代表的字串在SSS中出现的次数,即为siz[x]siz[x]siz[x]
那么我们直接枚举路径起点,然后跑dfsdfsdfs,并同时在SAMSAMSAM上跑
跑到一个节点就统计这个节点的sizsizsiz就行了

II

考虑点分治
求出树的重心rrr,然后求出经过重心的字符串的答案
因为设路径是s→ts\to ts→t
那么可以把路径拆成两部分,s→rs\to rs→r 和r→tr\to tr→t
我们考虑枚举SSS中的位置
计算有多少s→rs\to rs→r的路径是以当前位置结束的
计算有短少r→tr\to tr→t的路径是从当前位置开始的
乘起来就是:经过当前位置,且在当前位置时路径上正好是rrr的路径条数
加起来就是答案了
观察到两个计算是对称的
我们只需要建出SSS的反串的后缀自动机,然后在两个SAM上的操作就一模一样了
问题变化为求有多少s→rs\to rs→r的路径经过某个SAMSAMSAM中的节点
一个显然的思路是
我们从rrr开始向下dfsdfsdfs,并维护当前路径对应的是SAMSAMSAM中的哪个节点
然后把节点权值加一
那么出现在一个节点中的路径条数就是parent树上,当前点到根节点的权值之和
现在还有一个问题
注意到我们必须在路径前加字符,然后在SAM上跑
思考怎么维护这个
设当前路径长度为TTT,在SAMSAMSAM上的节点是xxx,某个xxx出现的位置是RxR_xRx​,新加入个字符时ccc
那么如果
T<len[x]T<len[x]T<len[x]那么加字符如果可以对应某个节点,那么一定还是xxx,直接判断
S[R[x]−T]S[R[x]-T]S[R[x]−T]是不是c就行了

T=len[x]T=len[x]T=len[x]
我们需要找一个节点yyy使得fa[y]=xfa[y]=xfa[y]=x,且S[R[y]−len[fa[y]]]=cS[R[y]-len[fa[y]]]=cS[R[y]−len[fa[y]]]=c
可以预处理出来

别忘了点分治时同一个子树要容斥掉
我们分析一波复杂度
因为每次统计答案必须遍历SSS一遍
所以复杂度
O(nlog⁡n+nm)O(n\log n+nm)O(nlogn+nm)
似乎更糟了
因此我们还有算法IIIIIIIII

III

设当前分治子树的大小为WWW
来一波根号分治
如果W≥nW\geq \sqrt nW≥n​按照算法IIIIII统计就可以了
如果W<nW<\sqrt nW<n​,按照n2n^2n2的做法做就行了
总复杂度O(nn+mn)O(n\sqrt n+m\sqrt n)O(nn​+mn​)
还有一个细节
就是容斥的时候如果子节点的子树小于n\sqrt nn​
也需要类似O(n2)O(n^2)O(n2)的做法去做
否则会被菊花图卡到O(nm)O(nm)O(nm)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5+7;
int read()
{int X=0;bool flag=0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')flag=1;ch=getchar();}while(ch>='0'&&ch<='9'){X=(X<<3)+(X<<1)+ch-'0';ch=getchar();}if(flag) return ~(X-1);return X;
}
struct edge
{int y,next;
}e[N];
int link[N],t=0;
bool vis[N];
void add(int x,int y)
{e[++t].y=y;e[t].next=link[x];link[x]=t;
}
int n,m,S;
char str[N];
int a[N];
int pos1[N],pos2[N];
struct SAM
{int len[N],tr[N][26],fa[N];int pre[N][26];int tot,last,siz[N];int fst[N],cnt[N];SAM(){tot=last=1;}inline void copy(int x,int y){fa[x]=fa[y];len[x]=len[y];for(int c=0;c<26;c++)tr[x][c]=tr[y][c];}inline void Extend(int c,int x){int p=last,np=last=++tot;len[np]=len[p]+1;siz[np]=1;fst[np]=x;while(p&&!tr[p][c]){tr[p][c]=np;p=fa[p]; }if(!p) fa[np]=1;else{int q=tr[p][c];if(len[q]==len[p]+1) fa[np]=q;else{int nq=++tot;copy(nq,q);len[nq]=len[p]+1;fa[np]=fa[q]=nq;while(p&&tr[p][c]==q){tr[p][c]=nq;p=fa[p];}}}}int c[N],SA[N];int s[N];inline void Build(){for(int i=1;i<=tot;i++) c[len[i]]++;for(int i=1;i<=tot;i++) c[i]+=c[i-1];for(int i=1;i<=tot;i++) SA[c[len[i]]--]=i;for(int i=tot;i>=2;i--){int x=SA[i];siz[fa[x]]+=siz[x];fst[fa[x]]=fst[x];pre[fa[x]][s[fst[x]-len[fa[x]]]]=x;}}inline void clear(){for(int i=1;i<=tot;i++)cnt[i]=0;}inline void spread(){for(int i=2;i<=tot;i++)cnt[SA[i]]+=cnt[fa[SA[i]]];}inline void Addin(int x,int fat,int p,int T){if(len[p]==T) p=pre[p][a[x]];else if(s[fst[p]-T]!=a[x]) p=0;if(!p) return;
//      cout<<x<<' '<<p<<endl;cnt[p]++;for(int i=link[x];i;i=e[i].next){int y=e[i].y;if(y==fat||vis[y]) continue;Addin(y,x,p,T+1);} }void Put(){for(int i=1;i<=tot;i++)for(int c=0;c<26;c++)if(tr[i][c]) cout<<i<<' '<<tr[i][c]<<' '<<c<<endl;}void push(){for(int i=2;i<=tot;i++)cout<<fa[i]<<' '<<i<<endl;}
}A,B;
int s[N];
int siz[N],son[N];
void getroot(int x,int pre,int &root,int tot)
{siz[x]=1;son[x]=0;for(int i=link[x];i;i=e[i].next){int y=e[i].y;if(y==pre||vis[y]) continue;getroot(y,x,root,tot);siz[x]+=siz[y];son[x]=max(son[x],siz[y]);}son[x]=max(son[x],tot-siz[x]);if(!root||son[root]>son[x]) root=x;
}
LL ans=0;
void getsiz(int x,int pre)
{siz[x]=1;for(int i=link[x];i;i=e[i].next){int y=e[i].y;if(y==pre||vis[y]) continue;getsiz(y,x);siz[x]+=siz[y];}
}
void getans(int x,int pre,int opt)
{A.clear();B.clear();if(pre){A.Addin(x,0,A.tr[1][a[pre]],1);B.Addin(x,0,B.tr[1][a[pre]],1);}else {A.Addin(x,0,1,0);B.Addin(x,0,1,0);}A.spread();B.spread();for(int i=1;i<=m;i++)ans+=opt*A.cnt[pos1[i]]*B.cnt[pos2[m-i+1]];
}
int q[N],top=0;
int par[N];
void getpoint(int x,int pre)
{q[++top]=x;for(int i=link[x];i;i=e[i].next){int y=e[i].y;if(y==pre||vis[y]) continue;par[y]=x;getpoint(y,x);}
}
int ban=0;
void getpath(int x,int pre,int p,int opt)
{p=A.tr[p][a[x]];if(!p) return;ans+=opt*A.siz[p];
//  cout<<x<<' '<<A.siz[p]<<endl;for(int i=link[x];i;i=e[i].next){int y=e[i].y;if(y==pre||vis[y]||y==ban) continue;getpath(y,x,p,opt);}
}
void Blocks(int x)
{top=0;getpoint(x,0);for(int i=1;i<=top;i++)getpath(q[i],0,1,1);
}void getpart(int x,int pre)
{top=0;int rt=x;par[rt]=pre;getpoint(x,pre);for(int i=1;i<=top;i++){int p=1,cur=q[i];while(cur!=pre){p=A.tr[p][a[cur]];cur=par[cur];}p=A.tr[p][a[cur]];getpath(x,0,p,-1);}
}
void Divide(int x,int tot)
{if(tot<=S){Blocks(x);return;}vis[x]=1;getans(x,0,1);
//  cout<<x<<"----"<<ans<<endl;for(int i=link[x];i;i=e[i].next){int y=e[i].y;if(vis[y]) continue;int z=0,T=siz[y];if(siz[y]>S) getans(y,x,-1);else getpart(y,x);
//      cout<<y<<' '<<ans<<endl;getroot(y,x,z,siz[y]);Divide(z,T);}
}int main()
{n=read();m=read();S=sqrt(m);for(int i=1;i<n;i++){int x=read(),y=read();add(x,y);add(y,x); }scanf("%s",str+1);for(int i=1;i<=n;i++)a[i]=str[i]-'a';scanf("%s",str+1);for(int i=1;i<=m;i++)s[i]=str[i]-'a';for(int i=1;i<=m;i++){A.Extend(s[i],i);pos1[i]=A.last;A.s[i]=s[i];}reverse(s+1,s+m+1);for(int i=1;i<=m;i++){B.Extend(s[i],i);pos2[i]=B.last;B.s[i]=s[i];}A.Build();B.Build();int root=0;getroot(1,0,root,n);Divide(root,n);cout<<ans;return 0;
}

[CTSC2010]珠宝商(点分治+根号分治+后缀自动机)相关推荐

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

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

  2. [HDU 6643] Ridiculous Netizens(点分治+根号分治+dp)

    HDU 6643 Ridiculous Netizens problem hdu6643 题目大意:给定一棵无根树,以及每个点的点权 wiw_iwi​. 定义一个连通块的价值为连通块内点的点权之积. ...

  3. 浅谈根号分治——暴力的美学

    根号分治 根号分治的概念 [模板]P3396 哈希冲突 CF103D Time to Raid Cowavans 根号分治的概念 根号分治是一种优化暴力算法. 我个人的理解就是这东西跟分块差不多.但应 ...

  4. 学习计划——根号分治

    根号分治 根号分治是一种思想,一般根据一个数(可以是数组的数,也可以是答案的数)分类,分为大于 s q r t ( n ) sqrt(n) sqrt(n)的部分和小于等于 s q r t ( n ) ...

  5. 【CTSC2010】珠宝商【后缀自动机】【点分治】【根号分治】

    题意:给一棵 nnn 个点的树,每个点有个字符,另给一个长度为 mmm 的特征串,求树上 n2n^2n2 条有向路径在特征串中出现的次数之和. n,m≤5×104n,m\leq 5\times 10^ ...

  6. 【BZOJ1921】【CTSC2010】珠宝商(点分治,后缀自动机)

    [BZOJ1921][CTSC2010]珠宝商(点分治,后缀自动机) 题面 洛谷 BZOJ权限题 题解 如果要我们做暴力,显然可以以某个点为根节点,然后把子树\(dfs\)一遍,建出特征串的\(SAM ...

  7. bzoj 1921: [Ctsc2010]珠宝商 后缀自动机+点分治

    题意 有一棵n个节点的树和一个长度为m的字符串S,树上每个节点有一个字符.问对于任意的有序数对(x,y),从x到y路径组成的字符串在S中出现次数的和. n,m<=50000n,m<=500 ...

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

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

  9. 【雅礼集训2017】字符串【后缀自动机】【数据分治】

    题意:给定一个字符串SSS和mmm个区间[li,ri][l_i,r_i][li​,ri​],qqq次询问,每次给定长度为kkk的字符串www和区间[a,b][a,b][a,b],求对于所有i∈[a,b ...

最新文章

  1. 实用技巧:Excel中的常见问题以及解决方法
  2. selenium3浏览器驱动安装设置方法
  3. FastDFS测试图片上传
  4. 元素类型为 “resultMap” 的内容必须匹配 “(constructor?,id*,result*,association*,collection*,discriminator?)”
  5. 每天一道LeetCode-----生命游戏
  6. [你必须知道的.NET]第二十六回:认识元数据和IL(下)
  7. c语言程序位置式pid算法,增量式与位置式PID算法(C语言实现与电机控制项目)...
  8. QT综合示例:QT串口通信
  9. 边缘检测中非极大值抑制简单解释
  10. LeetCode--81. 搜索旋转排序数组Ⅱ(遍历法,二分法)
  11. Golang之不可重入函数实现
  12. Python金融数据挖掘 第11章 复习思考题2 (聚类)选取中华人民共和国第六次人口普查的各地区人口数以及男女比例进行K-Means聚类分析。
  13. 微信小程序 — 生成二维码功能
  14. 计算机日志查询域用户登录记录,Windows域控制器身份验证登录日志记录和取证...
  15. 导出包含富文本内容和图片的word和pdf
  16. ESL3.6 几种线性回归方法比较学习笔记(含协方差相关系数概念)
  17. 从定时任务-到任务调度系统xxl-job
  18. C语言使用信号量解决生产者消费者模型的同步问题
  19. troublemaker中文谐音_饿狼传说谐音歌词
  20. 程序员的悲哀--动车追尾

热门文章

  1. 【百科】中华医书集成
  2. MongoDB中shell基本使用
  3. docker学习之docker镜像加速器
  4. Android Studio 模拟器Emulator 设置代理网络 | WIFI图标有叉号但是有网络解决方案
  5. 第四章:迭代器与生成器
  6. android高度比例布局,无处安放的AndroidTips:ConstraintLayout的比例布局
  7. java jpress,JPress导入Eclipse
  8. 国庆节发个国庆直播的小软件
  9. PLC培训班一般多少钱?
  10. 手把手教你如何PCB板材选型(一)