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,不知道排到哪了..难道别人都是树剖吗...看来有必要学 ...
随机推荐
- ubuntu 设置qt程序开机启动
1.建立一个桌面文件,forklift-app.desktop Name填写程序的名字 Exec执行程序的路径 [Desktop Entry] Version=1.0 Name=forklift-ap ...
- hbase的架构组成+hbase在create报错 -hue - mvn
0.hbase的组件 架构 参考:https://cloud.tencent.com/developer/article/1084209 各个组件的功能 参考:https://zhuanlan.zhi ...
- session到底是何时何地生成的
关于session,之前只是在用,从没考虑到底怎么生成的 今天有空我做了个实验,把监控了一下访问某网站第一二次的请求响应详细信息,终于搞明白了,好了,开始放图 这里发起一个请求,然后我们看下第一次请 ...
- 解决应用程序无法正常启动0xc0150002问题(转)
简述:使用VS2008写了一个MFC程序,结果传到别人的机子上(WIN7)出现应用程序正常初始化(0xc0150002)失败的问题.为什么我的机子上可以,而别人的机子上运行不了呢?下面是我找到的一个解 ...
- Java基础(五)
方法概述 方法的定义格式 什么是方法?方法就好像是一个工厂. 如奶牛场 原料:奶牛,饲料 产出物:牛奶,各种奶制品 程序当中的方法 参数(原料):进入方法的数据 返回值(产出物):从方法中出来的数据 ...
- python并发编程-进程池线程池-协程-I/O模型-04
目录 进程池线程池的使用***** 进程池/线程池的创建和提交回调 验证复用池子里的线程或进程 异步回调机制 通过闭包给回调函数添加额外参数(扩展) 协程*** 概念回顾(协程这里再理一下) 如何实现 ...
- 用css、如何让图片自动适应屏幕大小,不出现滚动条,不变形,兼容各个浏览器?急!!!
如果是个背景图的话,定义一个div,高100%,宽100%,里面放个img<div class='bg'> <img src="images/bg.jpg" al ...
- 01 Linux常用基本命令(一)
1.远程连接服务器 Xshell为例: ssh 用户名@IP地址 (ssh root@192.168.119.139) 查看服务器的IP地址: ifconfig (ip addr) 2.命令 1.ls ...
- react 不同环境配置不同域名
npm eject 先将配置文件暴露出来 将scripts中的build文件复制一份,改名为你需要的名字 将其中的 process.env.NODE_ENV 赋值为你需要的环境 在package.js ...
- 关于catopen函数
关于catopen函数: 参考网址:http://pubs.opengroup.org/onlinepubs/009695399/functions/catopen.html 1)编辑消息文件 [ro ...