LOJ

BZOJ

洛谷


点分治。考虑如何计算过\(rt\)的答案。

记\(pre[i]\)表示(之前的)子树内循环匹配了\(S\)的前缀\(i\)的路径有多少,\(suf[i]\)表示(之前的)子树内循环匹配了\(S\)的后缀\(i\)的路径有多少。

一个点如果能作为前缀\(dep\%m\)出现,然后\(s[rt]=s[dep\%m+1]\),就可以统计\(suf[m-dep\%m-1]\)的贡献。

作为后缀出现同理。

判某个深度\(dep\)是否是循环匹配了\(S\)的前缀\(i\),本来想的是首先\(dep\sim dep-m+1\)匹配了整个\(S\),其次再判\(dep-m\)匹配了前缀\(i\)。其实只要先把\(S\)变成长度为\(n\)的循环串,再对它求\(Hash\)就可以惹。这样每次插入一个字符时在字符串开头插入,只需要判断\(hs_{now}==hs[dep]\)。

判后缀就对应一下那个\(hs_{now}\)好了(每次在开头插入字符),即若出现了\(s[n-2],s[n-1],s[n]\),得到的Hash值是\(s[n],s[n-1],s[n-2]\)的,所以把串反过来求Hash值,判一下\(hs_{now}==hs'[dep]\)就好啦。

复杂度\(O(n\log n)\),这个数据范围应该卡不掉的叭?(应该。但是实际数据范围小惹?)

初始化\(1e6\)的\(pow\)数组还不如每次初始化\(n\)的=-=

另外单组数据是\(3\leq n,m\leq 1e5\)的。


//9940kb	2808ms
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define seed 31
#define gc() getchar()
#define MAXIN 500000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
typedef unsigned long long ull;
const int N=1e5+5; int m,s[N],ch[N],Enum,H[N],nxt[N<<1],to[N<<1],Min,root,sz[N],pre[N],suf[N],spre[N],ssuf[N],Maxd;
LL Ans;
ull pw[N],hs[N],hs2[N];
bool vis[N];
char IN[MAXIN],*SS=IN,*TT=IN; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline void AE(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
inline void GetHash(ull *a,int *s,int n,int m)
{
for(int i=m+1; i<=n; ++i) s[i]=s[i-m];
for(int i=1; i<=n; ++i) a[i]=a[i-1]*seed+s[i];
}
void FindRoot(int x,int fa,int tot)
{
int mx=0; sz[x]=1;
for(int i=H[x],v; i; i=nxt[i])
if(!vis[v=to[i]]&&v!=fa)
FindRoot(v,x,tot), sz[x]+=sz[v], sz[v]>mx&&(mx=sz[v]);
mx=std::max(mx,tot-mx);
if(mx<Min) Min=mx, root=x;
}
void DFS(int x,int fa,int dep,ull val,const int Chrt)
{
Maxd=std::max(Maxd,dep), val+=pw[dep-1]*ch[x];
int tmp=dep%m;
if(val==hs[dep]) ++pre[tmp], s[tmp+1]==Chrt&&(Ans+=ssuf[m-tmp-1]);
if(val==hs2[dep]) ++suf[tmp], s[m-tmp]==Chrt&&(Ans+=spre[m-tmp-1]);
for(int i=H[x],v; i; i=nxt[i])
if(!vis[v=to[i]]&&v!=fa) DFS(v,x,dep+1,val,Chrt);
}
void Solve(int x)
{
vis[x]=1; int mx=0; spre[0]=ssuf[0]=1;
// spre[0]=ch[x]==s[1], ssuf[0]=ch[x]==s[m];//这个不需要判的叭。因为统计贡献时要求s[p]=ch[rt]。
for(int i=H[x],v; i; i=nxt[i])
if(!vis[v=to[i]])
{
Maxd=0, DFS(v,x,1,0,ch[x]), Maxd=std::min(Maxd,m-1), mx=std::max(mx,Maxd);
for(int j=0,tmp=Maxd; j<=tmp; ++j) spre[j]+=pre[j], ssuf[j]+=suf[j], pre[j]=suf[j]=0;
}
for(int i=0; i<=mx; ++i) spre[i]=ssuf[i]=0;
for(int i=H[x],v; i; i=nxt[i])
if(!vis[v=to[i]]) Min=N, FindRoot(v,x,sz[v]), Solve(root);
} int main()
{
pw[0]=1;
for(int T=read(),mx=0; T--; )
{
int n=read(),m=read(); ::m=m;
Enum=0, memset(H,0,n+1<<2), memset(vis,0,n+1);
if(mx<n)
{
for(int i=mx+1; i<=n; ++i) pw[i]=pw[i-1]*seed;
mx=n;
}
register char c; while(!isalpha(c=gc())); ch[1]=c-65+1;
for(int i=2; i<=n; ++i) ch[i]=gc()-65+1;
for(int i=1; i<n; ++i) AE(read(),read());
while(!isalpha(c=gc())); s[1]=c-65+1;
for(int i=2; i<=m; ++i) s[i]=gc()-65+1;
GetHash(hs,s,n,m), std::reverse(s+1,s+1+m), GetHash(hs2,s,n,m), std::reverse(s+1,s+1+m);
Ans=0, Min=N, FindRoot(1,1,n), Solve(root), printf("%lld\n",Ans);
} return 0;
}

BZOJ.4598.[SDOI2016]模式字符串(点分治 Hash)的更多相关文章

  1. 【BZOJ4598】[Sdoi2016]模式字符串 树分治+hash

    [BZOJ4598][Sdoi2016]模式字符串 Description 给出n个结点的树结构T,其中每一个结点上有一个字符,这里我们所说的字符只考虑大写字母A到Z,再给出长度为m的模式串s,其中每 ...

  2. BZOJ4598: [Sdoi2016]模式字符串(点分治 hash)

    题意 题目链接 Sol 直接考虑点分治+hash匹配 设\(up[i]\)表示\(dep \% M = i\)的从下往上恰好与前\(i\)位匹配的个数 \(down\)表示\(dep \% M = i ...

  3. bzoj 4598: [Sdoi2016]模式字符串

    题目描述 给出n个结点的树结构T,其中每一个结点上有一个字符,这里我们所说的字符只考虑大写字母A到Z,再给出长度为m的模式串s,其中每一位仍然是A到z的大写字母. Alice希望知道,有多少对结点&l ...

  4. Bzoj4598: [Sdoi2016]模式字符串 点分治 哈希

    国际惯例的题面:这种关于树上路径的题,我也没什么好办法,只好点分治.考虑当前分治重心为root,如何统计经过分治重心的路径的答案.我们令prf[i]表示某个点到root的路径(不含root)已经循环匹 ...

  5. P4075 [SDOI2016]模式字符串

    总结 P4075 [SDOI2016]模式字符串 题目描述 给出n个结点的树结构T,其中每一个结点上有一个字符,这里我们所说的字符只考虑大写字母A到Z,再给出长度为m的模式串s,其中每一位仍然是A到z ...

  6. BZOJ4598 [Sdoi2016]模式字符串 【点分治 + hash】

    题目 给出n个结点的树结构T,其中每一个结点上有一个字符,这里我们所说的字符只考虑大写字母A到Z,再给出长度为m 的模式串s,其中每一位仍然是A到z的大写字母.Alice希望知道,有多少对结点< ...

  7. bzoj4598: [Sdoi2016]模式字符串

    Description 给出n个结点的树结构T,其中每一个结点上有一个字符,这里我们所说的字符只考虑大写字母A到Z,再给出长度为m 的模式串s,其中每一位仍然是A到z的大写字母.Alice希望知道,有 ...

  8. [SDOI2016]模式字符串

    Description 给出n个结点的树结构T,其中每一个结点上有一个字符,这里我们所说的字符只考虑大写字母A到Z,再给出长度为m的模式串s,其中每一位仍然是A到z的大写字母.Alice希望知道,有多 ...

  9. [SDOI2016] 模式字符串 (BZOJ4598 & VIJOS1995)

    首先直接点分+hash就可以做,每个点用hash判断是否为S重复若干次后的前缀或后缀,每个子树与之前的结果O(m)暴力合并.在子树大小<m时停止分治,则总复杂度为O(nlog(n/m)). 问题 ...

随机推荐

  1. MQ服务器奔溃解决过程

    1.MQ服务器崩溃调节: 今天具安卓前端反应, 从昨天下午开始线上服务器使用 电话号码登陆和 使用电话号码注册功能不能使用, 经过前端仔细排查怀疑是后端问题,之后经过与ios前端 确认, 定位为后端服 ...

  2. Numpy 系列(九)- 结构化数组

      简介 之前我们操作Numpy的数组时,都是通过索引来操作的.针对二维数组,使用索引可以完成对行.列的操作.但是这是非常不直观的.可以把二维数组想象成一个excel表格,如果表格没有列名,操作起来会 ...

  3. [物理学与PDEs]第5章习题1 矩阵的极分解

    证明引理 2. 1. 证明: (1)  先证明存在正交阵 ${\bf P},{\bf Q}$ 及对角阵 ${\bf D}$ 使得 $$\bex {\bf F}={\bf P}{\bf D}{\bf Q ...

  4. EffectiveC++ 第5章 实现

    我根据自己的理解,对原文的精华部分进行了提炼,并在一些难以理解的地方加上了自己的"可能比较准确"的「翻译」. Chapter 5 实现 Implementations 适当提出属于 ...

  5. EffectiveC++ 第7章 模板与泛型编程

    我根据自己的理解,对原文的精华部分进行了提炼,并在一些难以理解的地方加上了自己的"可能比较准确"的「翻译」. Chapter 7 模版与泛型编程 Templates and Gen ...

  6. java(9)类和对象

    一.理解什么是类和对象 万事万物皆对象 1.1.属性——对象具有的特征(特点) 1.2.方法——对象可执行的操作(能干什么事) 1.3.对象的定义: 是一个客观存在的,看的见或摸得着的实体,由属性和方 ...

  7. react-router v4中 HashRouter 和 BrowserRouter的使用

    遇到的问题 项目中控制路由跳转使用的是BrowserRouter,代码如下: ReactDOM.render(( <BrowserRouter> <div className=&qu ...

  8. 练习:javascript-setInterval动画运动平移,定时器动画练习

    常见问题:定时器加速问题,每次点击会启动一个定时器,解决先清除定时器 <!DOCTYPE html> <html lang="en"> <head&g ...

  9. ng-app&data-ng-app

    来源stackoverflow 区别:在验证html5时,ng-app会抛出一个错误,而对带data-前缀的特性不会抛出.其它方面这两个属性一样.

  10. ES--06

    第51.初识搜索引擎_上机动手实战多搜索条件组合查询 课程大纲 GET /website/article/_search{ "query": { "bool": ...