BZOJ3881:[Coci2015]Divljak AC自动机+树链求并
Mar 06, 2015 11:18:58 AM
思路:
AC自动机的Fail树有很多奇妙的性质.
例如串\(a\)是串\(b\)在Fail树上的祖先,那么\(a\)在\(b\)中应该出现\(dep[b]-dep[a]\)次.
(不过好像跟这题没什么关系)
把Alice的\(n\)个字符串插入AC自动机.
然后每插入一个修改串,都动态维护一下每个自动机中的节点,如果出现在修改串中,则该节点答案+1.
我们用修改串在自动机上走,每走到一个节点,我们都想要这个节点在Fail树上到根的路径上的答案均+1.
遗憾的是,这样有可能重复!
利用树链的并.
将所有要处理的节点按照dfs序排序,然后首先把它们到根的路径+1.
随后将相邻两个节点的LCA到根的路径-1.
非常科学.
怎么加?树链剖分?等TLE吧.
直接将标记累加在节点上,询问时就询问子树内的标记之和.
这样只需用树状数组维护DFS序即可.
可惜卡常数!
用倍增LCA会超时.
于是我们换用RMQlca,并用ZKW线段树维护.
估计ST表预处理也会超时.
反正总算还是水过去了.
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define N 2002010
int ch[N][26],fail[N],ins[N],cnt;
char s[N];
int head[N],next[N],end[N];
inline void addedge(int a,int b){
static int q=1;end[q]=b,next[q]=head[a],head[a]=q++;
}
int in[N],out[N],dep[N],tclock;
void dfs(int x){
in[x]=++tclock;
for(int j=head[x];j;j=next[j])
dep[end[j]]=dep[x]+1,dfs(end[j]);
out[x]=tclock;
}
namespace Lca_system{
int Seq[N<<1],a[8383608],M,ins[N],cnt;
inline void build_dfs(int x){
ins[x]=++cnt;
Seq[cnt]=x;
for(int j=head[x];j;j=next[j]){
build_dfs(end[j]);
Seq[++cnt]=x;
}
}
inline int Min(int x,int y){
if(x==-1||y==-1)return x==-1?y:x;
return dep[x]<dep[y]?x:y;
}
inline void init(){
build_dfs(0);
for(M=1;M<cnt+2;M<<=1);
memset(a,-1,sizeof a);
for(int i=1;i<=cnt;++i)a[M+i]=Seq[i];
for(int i=M-1;i>=1;--i)a[i]=Min(a[2*i],a[2*i+1]);
}
inline int ask(int tl,int tr){
int re=-1;
for(tl+=M-1,tr+=M+1;tl^tr^1;tl>>=1,tr>>=1){
if(~tl&1)re=Min(re,a[tl^1]);
if(tr&1)re=Min(re,a[tr^1]);
}
return re;
}
inline int lca(int x,int y){
x=ins[x],y=ins[y];
if(x>y)swap(x,y);
return ask(x,y);
}
}
int seq[N],num;
int A[N];
inline void modify(int x,int c){
for(;x<=cnt+1;x+=x&-x)A[x]+=c;
}
inline int ask(int x){
int re=0;for(;x;x-=x&-x)re+=A[x];return re;
}
inline bool cmp(const int&x,const int&y){return in[x]<in[y];}
inline int git(){
int c,re;
while(!isdigit(c=getchar()));
re=c-'0';
while(isdigit(c=getchar()))re=(re<<1)+(re<<3)+c-'0';
return re;
}
char buf[100010*6],*o=buf;
inline void print(int x){
static int s[100];int top=0;
if(!x)*o++=48;else{for(;x;x/=10)s[++top]=x%10;for(int i=top;i>=1;--i)*o++=48+s[i];}
*o++='\n';
}
int main(){
int n=git();
register int i,j;
int len,p;
for(i=1;i<=n;++i){
scanf("%s",s);
len=strlen(s),p=0;
for(j=0;j<len;++j){
if(!ch[p][s[j]-'a'])ch[p][s[j]-'a']=++cnt;
p=ch[p][s[j]-'a'];
}
ins[i]=p;
}
queue<int>q;
for(i=0;i<26;++i)if(ch[0][i])q.push(ch[0][i]);
int u,v,r;
while(!q.empty()){
u=q.front(),q.pop();
for(i=0;i<26;++i)if((v=ch[u][i])){
q.push(v);
for(r=fail[u];r&&!ch[r][i];r=fail[r]);
fail[v]=ch[r][i];
}
}
for(i=1;i<=cnt;++i)addedge(fail[i],i);
dep[0]=1,dfs(0);
Lca_system::init();
int Q=git();
int qte,x;
while(Q--){
qte=git();
if(qte==1){
scanf("%s",s);
len=strlen(s),p=0,num=0;
for(i=0;i<len;++i){
while(p&&!ch[p][s[i]-'a'])p=fail[p];
p=ch[p][s[i]-'a'];
if(p)seq[++num]=p;
}
sort(seq+1,seq+num+1,cmp);
for(i=1;i<=num;++i)modify(in[seq[i]],1);
for(i=1;i<num;++i)modify(in[Lca_system::lca(seq[i],seq[i+1])],-1);
}
else{
x=git();
print(ask(out[ins[x]])-ask(in[ins[x]]-1));
}
}
fwrite(buf,1,o-buf,stdout);
return 0;
}
BZOJ2144:跳跳棋 LCA
Jan 29, 2015 09:37:24 PM
思路:
对于一个状态\((x,y,z)(x<y<z)\),我们发现可行的操作仅有三种:
(1)将\(y\)向左移动
(2)将\(y\)向右移动
(3)将\(x,z\)中离\(y\)比较近的一个以\(y\)为中心移动.(当\(y-x=z-y\)时不能这样做)
那么有人就要问了,还有离\(y\)比较远的以\(y\)为中心移动的情况呢?这必然与(1)(2)中的一个等价.
因此,我们可以将状态建成一棵树,对于一个状态,(1)(2)得到的是这个状态的左右儿子,(3)得到的是这个状态的父亲.
那么就很容易处理了.我们类似寻找LCA的过程不断向上爬即可.但我们不是暴力的向上找父亲,而是发现连续的若干次找父亲其实就是一个做gcd的过程.利用Euclid加速即可.
#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
struct Pos{
int x,y,z;
Pos(){}
Pos(int _x,int _y,int _z):x(_x),y(_y),z(_z){}
bool operator!=(const Pos&B)const{return x!=B.x||y!=B.y||z!=B.z;}
bool operator==(const Pos&B)const{return x==B.x&&y==B.y&&z==B.z;}
};
LL updep;
inline Pos up(int x,int y,int z,LL d){
if(d==0)return Pos(x,y,z);
if(y-x==z-y)return Pos(x,y,z);
int d1=y-x,d2=z-y,step;
if(d1>d2){
if(d1%d2==0){
step=d1/d2-1;
if(d<=step)return updep+=d,Pos(x,x+d1-d*d2,x+d1-d*d2+d2);
else return updep+=step,up(x,x+d2,x+2*d2,d-step);
}
else{
step=d1/d2;
if(d<=step)return updep+=d,Pos(x,x+d1-d*d2,x+d1-d*d2+d2);
else return updep+=step,up(x,x+d1%d2,x+d1%d2+d2,d-step);
}
}
else{
if(d2%d1==0){
step=d2/d1-1;
if(d<=step)return updep+=d,Pos(z-(d2-d*d1)-d1,z-(d2-d*d1),z);
else return updep+=step,up(z-2*d1,z-d1,z,d-step);
}
else{
step=d2/d1;
if(d<=step)return updep+=d,Pos(z-(d2-d*d1)-d1,z-(d2-d*d1),z);
else return updep+=step,up(z-d2%d1-d1,z-d2%d1,z,d-step);
}
}
}
inline Pos up(const Pos&p,LL d){return up(p.x,p.y,p.z,d);}
inline void Sort(int&x,int&y,int&z){
int d[3]={x,y,z};sort(d,d+3);x=d[0],y=d[1],z=d[2];
}
int main(){
int x1,y1,z1,x2,y2,z2;
scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);Sort(x1,y1,z1),Sort(x2,y2,z2);
LL dep1,dep2;
updep=0;Pos Root1=up(x1,y1,z1,1<<30);dep1=updep;
updep=0;Pos Root2=up(x2,y2,z2,1<<30);dep2=updep;
if(Root1!=Root2)puts("NO");
else{
puts("YES");
if(dep1<dep2)swap(dep1,dep2),swap(x1,x2),swap(y1,y2),swap(z1,z2);
Pos now1=up(x1,y1,z1,dep1-dep2),now2=Pos(x2,y2,z2);
if(now1==now2)printf("%d",dep1-dep2);
else{
int L=0,R=dep2,mid;
while(L<R){
mid=(L+R+1)>>1;
if(up(now1,mid)!=up(now2,mid))L=mid;else R=mid-1;
}
printf("%d",dep1-dep2+2*(L+1));
}
}
#ifndef ONLINE_JUDGE
system("pause");
#endif
return 0;
}