题意:有一棵树,树上每个结点上有一个字母,有两种操作:

1)询问树上两点u,v间有向路径上有多少个字母和某个固定的字符串相匹配

2)将结点u的字母修改为x

树剖+线段,暴力维护前缀和后缀哈希值(正反都要维护)以及区间内匹配的个数,合并两区间时判断一下跨过分界点的情况就行了。由于被匹配的字符串长度不超过100,所以最多只需维护长度为100的前缀/后缀。

但即使这样复杂度也足足有$O(100nlog^2n)$啊,这常数是得有多小才能过掉...

注意各种条件判断和细节处理,还有就是这题内存比较吃紧,使用动态开点可以节省一半的内存。

 #include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int N=1e5+,M=;
int hd[N],n,ne,m,Len,fa[N],son[N],siz[N],dep[N],top[N],dfn[N],rnk[N],tot,rt,ls[N<<],rs[N<<],tot2;
ll H,pm[N];
char s1[N],s2[N];
struct D {
int len,sum;
ll L[],R[];
D(int ch=) {
len=;
sum=(Len==&&ch==H);
L[]=R[]=ch;
}
} tr[N<<][];
struct E {int v,nxt;} e[N<<];
D operator+(const D& a,const D& b) {
if(a.L[]==)return b;
if(b.L[]==)return a;
D c;
c.len=a.len+b.len;
c.sum=a.sum+b.sum;
for(int i=; i<=min(,c.len); ++i) {
if(i<=a.len)c.L[i]=a.L[i];
else c.L[i]=a.L[a.len]*pm[i-a.len]+b.L[i-a.len];
if(i<=b.len)c.R[i]=b.R[i];
else c.R[i]=a.R[i-b.len]*pm[b.len]+b.R[b.len];
}
for(int i=max(,Len-b.len); i<=a.len&&i<=Len-; ++i)if(a.R[i]*pm[Len-i]+b.L[Len-i]==H)c.sum++;
return c;
}
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void dfs1(int u,int f,int d) {
fa[u]=f,son[u]=,siz[u]=,dep[u]=d;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u])continue;
dfs1(v,u,d+),siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs2(int u,int tp) {
top[u]=tp,dfn[u]=++tot,rnk[dfn[u]]=u;
if(son[u])dfs2(son[u],top[u]);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u]||v==son[u])continue;
dfs2(v,v);
}
}
#define mid ((l+r)>>1)
void pu(int u) {
tr[u][]=tr[ls[u]][]+tr[rs[u]][];
tr[u][]=tr[rs[u]][]+tr[ls[u]][];
}
void upd(int p,int x,int& u=rt,int l=,int r=tot) {
if(l==r) {tr[u][]=tr[u][]=D(x); return;}
p<=mid?upd(p,x,ls[u],l,mid):upd(p,x,rs[u],mid+,r);
pu(u);
}
void build(int& u=rt,int l=,int r=tot) {
u=++tot2;
if(l==r) {tr[u][]=tr[u][]=D(s2[rnk[l]-]); return;}
build(ls[u],l,mid),build(rs[u],mid+,r),pu(u);
}
D qry(int L,int R,int f,int u=rt,int l=,int r=tot) {
if(l>=L&&r<=R)return tr[u][f];
if(l>R||r<L)return D();
if(f==)return qry(L,R,f,ls[u],l,mid)+qry(L,R,f,rs[u],mid+,r);
else return qry(L,R,f,rs[u],mid+,r)+qry(L,R,f,ls[u],l,mid);
}
int qry2(int u,int v) {
D L=D(),R=D(),M;
for(; top[u]!=top[v];) {
if(dep[top[u]]>dep[top[v]])L=L+qry(dfn[top[u]],dfn[u],),u=fa[top[u]];
else R=qry(dfn[top[v]],dfn[v],)+R,v=fa[top[v]];
}
if(dep[u]>dep[v])M=qry(dfn[v],dfn[u],);
else M=qry(dfn[u],dfn[v],);
return (L+M+R).sum;
}
int main() {
pm[]=;
for(int i=; i<N; ++i)pm[i]=pm[i-]*M;
memset(hd,-,sizeof hd),ne=;
scanf("%d%d",&n,&m);
scanf("%s%s",s1,s2),Len=strlen(s1);
H=;
for(int i=; i<Len; ++i)H=H*M+s1[i];
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
dfs1(,,),dfs2(,);
build(rt);
while(m--) {
int f,u,v;
char ch;
scanf("%d",&f);
if(f==)scanf("%d%d",&u,&v),printf("%d\n",qry2(u,v));
else scanf("%d %c",&u,&ch),upd(dfn[u],ch);
}
return ;
}

当然还有$O(100nlogn)$的LCT毒瘤做法,代码比树剖短,需要特判的地方少,而且更省内存,但是常数巨大,需要优化下常数才能过,比如改个引用传递什么的。

 #include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int N=1e5+,M=;
int n,m,a[N],fa[N],ch[N][],flp[N],sta[N],tp,Len;
ll H,pm[N];
char s1[N],s2[N];
struct D {
int len,sum;
ll L[],R[];
D(int ch=) {
len=;
sum=(Len==&&ch==H);
L[]=R[]=ch;
}
} tr[N][];
D operator+(const D& a,const D& b) {
if(a.L[]==)return b;
if(b.L[]==)return a;
D c;
c.len=a.len+b.len;
c.sum=a.sum+b.sum;
for(int i=; i<=min(,c.len); ++i) {
if(i<=a.len)c.L[i]=a.L[i];
else c.L[i]=a.L[a.len]*pm[i-a.len]+b.L[i-a.len];
if(i<=b.len)c.R[i]=b.R[i];
else c.R[i]=a.R[i-b.len]*pm[b.len]+b.R[b.len];
}
for(int i=max(,Len-b.len); i<=a.len&&i<=Len-; ++i)if(a.R[i]*pm[Len-i]+b.L[Len-i]==H)c.sum++;
return c;
}
#define l(u) ch[u][0]
#define r(u) ch[u][1]
void rev(int u) {flp[u]^=,swap(l(u),r(u)),swap(tr[u][],tr[u][]);}
void pu(int u) {
tr[u][]=tr[l(u)][]+D(s2[u])+tr[r(u)][];
tr[u][]=tr[r(u)][]+D(s2[u])+tr[l(u)][];
}
void pd(int u) {if(flp[u])rev(l(u)),rev(r(u)),flp[u]=;}
int sf(int u) {return u==r(fa[u]);}
bool isrt(int u) {return u!=l(fa[u])&&u!=r(fa[u]);}
void rot(int u) {
int v=fa[u],f=sf(u);
if(!isrt(v))ch[fa[v]][sf(v)]=u;
ch[v][f]=ch[u][f^],fa[ch[v][f]]=v;
fa[u]=fa[v],ch[u][f^]=v,fa[v]=u,pu(v);
}
void splay(int u) {
sta[tp=]=u;
for(int v=u; !isrt(v); v=fa[v])sta[++tp]=fa[v];
for(; ~tp; pd(sta[tp--]));
for(; !isrt(u); rot(u))if(!isrt(fa[u])&&sf(fa[u])==sf(u))rot(fa[u]);
pu(u);
}
void access(int u) {for(int v=; u; splay(u),r(u)=v,pu(u),u=fa[v=u]);}
void makert(int u) {access(u),splay(u),rev(u);}
void link(int u,int v) {makert(u),fa[u]=v;}
void join(int u,int v) {makert(u),access(v),splay(v);}
void upd(int u,int ch) {splay(u),s2[u]=ch;}
int qry(int u,int v) {join(u,v); return tr[v][].sum;}
int main() {
pm[]=;
for(int i=; i<N; ++i)pm[i]=pm[i-]*M;
scanf("%d%d",&n,&m);
scanf("%s%s",s1,s2+),Len=strlen(s1);
H=;
for(int i=; i<Len; ++i)H=H*M+s1[i];
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
link(u,v);
}
while(m--) {
int f,u,v;
char ch;
scanf("%d",&f);
if(f==)scanf("%d%d",&u,&v),printf("%d\n",qry(u,v));
else scanf("%d %c",&u,&ch),upd(u,ch);
}
return ;
}

Gym - 101908H Police Hypothesis (树链剖分/LCT+字符串哈希)的更多相关文章

  1. Bzoj 2243: [SDOI2011]染色 树链剖分,LCT,动态树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5020  Solved: 1872[Submit][Status ...

  2. Bzoj 1036: [ZJOI2008]树的统计Count 树链剖分,LCT

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 11102  Solved: 4490[Submit ...

  3. [CodeVS2370] 小机房的树 (LCA, 树链剖分, LCT)

    Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花 ...

  4. 【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分/LCT+贪心

    Description 给你一个有n个点的树,每个点的包括一个位运算opt和一个权值x,位运算有&,l,^三种,分别用1,2,3表示. 每次询问包含三个数x,y,z,初始选定一个数v.然后v依 ...

  5. BZOJ2243: [SDOI2011]染色(树链剖分/LCT)

    Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段), 如 ...

  6. bzoj 4817: [Sdoi2017]树点涂色【树链剖分+LCT】

    非常妙的一道题. 首先对于操作一"把点x到根节点的路径上所有的点染上一种没有用过的新颜色",长得是不是有点像LCT中的access操作?进而发现,如果把同一颜色的点连起来作为LCT ...

  7. [BZOJ2157]旅游(树链剖分/LCT)

    树剖裸题,当然LCT也可以. 树剖: #include<cstdio> #include<algorithm> #define ls (x<<1) #define ...

  8. Gym - 102040F Path Intersection (树链剖分+线段树)

    题意:给出棵树上的k条路径,求这些路径的公共点数量. 将每条路径上的点都打上标记,被标记过k次的点就是公共点了.由于公共点形成的区间是连续的,因此直接在线段树上暴搜即可在$O(logn)$求出一条链上 ...

  9. 【BZOJ】1036: [ZJOI2008]树的统计Count(lct/树链剖分)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1036 lct: (ps:为嘛我的那么慢T_T,不知道排到哪了..难道别人都是树剖吗...看来有必要学 ...

随机推荐

  1. 如何在WIN7下安装虚拟机linux系统

    需要支持多个平台的IT管理员经常会遇到如何在Windows 7计算机上安装Linux的问题.幸运的是有多种方法可供选择:双系统.Linux虚拟机和U盘引导. 当需要用到Windows 7和Linux时 ...

  2. 手写k-means算法

    作为聚类的代表算法,k-means本属于NP难问题,通过迭代优化的方式,可以求解出近似解. 伪代码如下: 1,算法部分 距离采用欧氏距离.参数默认值随意选的. import numpy as np d ...

  3. 应用安全 - PHP - CMS - EmpireCMS - 漏洞 - 汇总

    2006 Empire CMS <= 3.7 (checklevel.php) Remote File Include Vulnerability Empire CMS Checklevel.P ...

  4. dll的封装和使用

    背景 在windows平台下,要实现函数的封装,一般采用的是dll动态库的形式 实现了函数的封装就意味着实现过程的隐藏 可以实现跨平台和跨语言的使用 实施步骤 生成dll动态库 在VS中生成新项目,应 ...

  5. SolidWorks学习笔记1

    鼠标 修改缩放方向 点击工具->选项->视图,点击第一条,翻转鼠标滚轮缩放方向. 平移:Ctrl+ 中键 或者右键空白处 菜单选择平移 缩放:Shift+中键 或者右键空白处 菜单选择放大 ...

  6. filter方法常用过滤条件

    #encoding: utf-8 from sqlalchemy import create_engine,Column,Integer,String,Float,func,and_,or_ from ...

  7. 小记---------CDH版大数据组件--clouderManager UI界面

    启动 /opt/cm-5.14.0/etc/init.d/clouder-scm-server start /opt/cm-5.14.0/etc/init.d/clouder-scm-agent st ...

  8. Vue 中 $attrs 的使用

    名词解释: $attrs--继承所有的父组件属性(除了prop传递的属性.class 和 style ) inheritAttrs:默认值true,继承所有的父组件属性(除props的特定绑定)作为普 ...

  9. jinja2 模板相关

    安装 pip install jinja2 配置模板 settings.py 60行左右 TEMPLATES = [ { 'BACKEND': 'django.template.backends.dj ...

  10. The Digits String

    https://ac.nowcoder.com/acm/contest/338/L 题解: 当n==1时,0-9填上的话,对4取余,分别是余数为0的3个,1的3个,2的2个,3的2个: 当n==2时, ...