Gym - 101908H Police Hypothesis (树链剖分/LCT+字符串哈希)
题意:有一棵树,树上每个结点上有一个字母,有两种操作:
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+字符串哈希)的更多相关文章
- Bzoj 2243: [SDOI2011]染色 树链剖分,LCT,动态树
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 5020 Solved: 1872[Submit][Status ...
- Bzoj 1036: [ZJOI2008]树的统计Count 树链剖分,LCT
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 11102 Solved: 4490[Submit ...
- [CodeVS2370] 小机房的树 (LCA, 树链剖分, LCT)
Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花 ...
- 【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分/LCT+贪心
Description 给你一个有n个点的树,每个点的包括一个位运算opt和一个权值x,位运算有&,l,^三种,分别用1,2,3表示. 每次询问包含三个数x,y,z,初始选定一个数v.然后v依 ...
- BZOJ2243: [SDOI2011]染色(树链剖分/LCT)
Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段), 如 ...
- bzoj 4817: [Sdoi2017]树点涂色【树链剖分+LCT】
非常妙的一道题. 首先对于操作一"把点x到根节点的路径上所有的点染上一种没有用过的新颜色",长得是不是有点像LCT中的access操作?进而发现,如果把同一颜色的点连起来作为LCT ...
- [BZOJ2157]旅游(树链剖分/LCT)
树剖裸题,当然LCT也可以. 树剖: #include<cstdio> #include<algorithm> #define ls (x<<1) #define ...
- Gym - 102040F Path Intersection (树链剖分+线段树)
题意:给出棵树上的k条路径,求这些路径的公共点数量. 将每条路径上的点都打上标记,被标记过k次的点就是公共点了.由于公共点形成的区间是连续的,因此直接在线段树上暴搜即可在$O(logn)$求出一条链上 ...
- 【BZOJ】1036: [ZJOI2008]树的统计Count(lct/树链剖分)
http://www.lydsy.com/JudgeOnline/problem.php?id=1036 lct: (ps:为嘛我的那么慢T_T,不知道排到哪了..难道别人都是树剖吗...看来有必要学 ...
随机推荐
- 03 vue项目结构
上一篇已介绍根据vue-cli创建项目,本篇介绍根据vue-cli官方脚手架创建的项目的项目结构. 一.图看结构 build [webpack配置] webpack相关配置,都已经配 ...
- 【DSP开发】回马枪要你命 德州仪器发布最强ARM芯片Keystone II
之前许多传闻称德州仪器将会彻底放弃OMAP系列ARM处理器,从此离开手持设备的江湖.如果你信以为真,那可就太小看德州仪器这个老狐狸了--要知道德州仪器诞生的比Intel都还早几年.三小时前,德州仪器宣 ...
- jmeter性能测试总结
一.性能测试问题记录: Ⅰ.秒杀的失败率了在96.45%,原因 Query对于 活动的秒杀采用的是0.5秒,刷新缓存的策略在活动中优惠券被秒杀一空 下架前,短暂的时间内仍能够查询到 这个活动架构中采用 ...
- AKKA文档2.3(java版)—什么是角色
原文:http://doc.akka.io/docs/akka/2.3.5/general/actors.html译者:Vitas 什么是角色? 前面角色系统一节介绍了一群角色如何形成一个层次结构,并 ...
- 第六次java实验报告
Java实验报告 班级 计科二班 学号20188437 姓名 何磊 完成时间 2019/10/17 评分等级 实验四 类的继承 实验目的 理解异常的基本概念: 掌握异常处理方法及熟悉常见异常的捕获方法 ...
- [python] 格式化方法 format
先介绍包含的所有规则 花括号声明{}:用于渲染前的参数引用声明,花括号里可以用数字代表引用参数的序号,或者变量名直接引用. 从format参数引入的变量名 冒号: 字符位数声明 空白自动填补符的声明 ...
- [转帖]linux下使用 du查看某个文件或目录占用磁盘空间的大小
linux下使用 du查看某个文件或目录占用磁盘空间的大小 du -ah --max-depth= 去年用过一次 后来忘记了.. 命令这个东西 熟能生巧.. https://www.cnblogs.c ...
- Luogu P3511 [POI2010]MOS-Bridges
题目 二分答案然后混合图欧拉回路,因为没有SPJ所以就没写了,怕写了有锅.
- Docker学习1
命名空间(Namesaoces):Linux内核提供的一种对进程资源隔离的机制,例如网络.进程.挂载点等资源. 控制组(CGroups):Linux内核提供的一种限制进程资源的机制:例如CPU.内存等 ...
- jsp页面中使用 splitfn:split注意事项
我们有一个 字段存储内容是 xxxx意见~~@~~是 在页面上需要分开显示,格式为 xxx意见 是 使用 ${fn:split(comments, '~~@~~')[1]} 来分割是发现出现@符文字 ...