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,不知道排到哪了..难道别人都是树剖吗...看来有必要学 ...
随机推荐
- SQL- 将一张表的数据插入到另一张表,表结构不一致(加条件)
公司业务需要,在对表进行操作的时候将操作人和操作记录记录到日志表里.记录下来以供参考和学习. 首先准备两张测试表:Info以及InfoLog 1.表结构相同的情况下: insert into Info ...
- python-Web-django-富文本编辑器
views: def gbook(request): '''''' text = request.POST.get('text') soup = BeautifulSoup(text, "h ...
- Windows下的开发辅助神器——Chocolate Package Manager
Windows下的开发辅助神器——Chocolate Package Manager:https://juejin.im/post/5c6cb3acf265da2dc4537235 Windows上的 ...
- MessageBox显示位置
假设存在2个窗口类CImDlg与CChatDlg,如果希望MessageBox跟随CChatDlg,方法是 CChatDlg *pDlg = xxx; pDlg->MessageBox();
- Linux基础训练题型(下)
8.在题3的基础上,使用命令调换passwd文件里root位置和/bin/bash位置?即将所有的第一列和最后一列位置调换? 例: 默认:root:x:0:0:root:/root:/bin/bash ...
- [转帖]PostgreSQL与MySQL比较 From 2010年
PostgreSQL与MySQL比较 [复制链接] http://bbs.chinaunix.net/thread-1688208-1-1.html osdba 稍有积蓄 好友 博客 消息 论坛徽章 ...
- CNN中feature map、卷积核、卷积核的个数、filter、channel的概念解释
CNN中feature map.卷积核.卷积核的个数.filter.channel的概念解释 参考链接: https://blog.csdn.net/xys430381_1/article/detai ...
- EBS描述性弹性域及键弹性域总结
一.描述性弹性域 前言介绍: 描述性弹性域的实质就是系统预留自定字段,系统可以使用说明性弹性域来获取业务所特有的重要附加信息.系统可能自定义说明性弹性域,以显示存储更多信息的字段,提供一套完整的“自定 ...
- C# 面向对象5 this关键字和析构函数
this关键字 1.代表当前类的对象 2.在类当中显示的调用本类的构造函数(避免代码的冗余) 语法: ":this" 以下一个参数的构造函数调用了参数最全的构造函数!并赋值了那些不 ...
- oa_mvc_easyui_分页(4)
1.数据层的编写 NewListInfoDal.cs: GetPageEntityList方法,根据start,end取出数据 --row_number() over()函数查询 LoadEntity ...