【WC2016模拟】打击目标

Description

这里写图片描述

Input

这里写图片描述

Output
这里写图片描述

Sample Input

3 0
xyz xy x
1 2
2
1 3 xyz
2 3 zzzzxy

Sample Output

3
2

Data Constraint
这里写图片描述

题解

这一题在比赛的时候我已经想到了和题解基本一样的做法,然而因为不会树剖(雾,所以只打了50point,结果数据超级水,要不是开小了数组就AC了QvQ
下午赶紧学习了一下树剖,然后发现是一个超级简单的东西,就是维护出每一个点对应的重链的上面的顶点是什么,然后看哪边深就跳哪边就好了

那么这一题怎么做呢?

收先看到多串匹配条件反射的就想到了SA和AC自动机
看了一下发现就是一个AC自动机
某一个串经过的自动机中某一节点沿着fail能跳到的就是它的子串

那么我们不妨对自动机中的每一个点挂一个可持久化线段树,然后沿着fail边不断的更新就好了

询问的话因为树剖了一下所以只有log段,复杂度应该是两个log的

贴代码

打的不是很优美。。。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fo1(i,b,a) for(i=b;i>=a;i--)
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;

const int maxn=1e5+5;

int tree[maxn*10][27],fail[maxn*10],h[maxn*10];
int tr[maxn*50][3];
int ff[maxn][18];
int cc[maxn*10],de[maxn],dep[maxn*10];
int fi[maxn],ne[maxn*2],dui[maxn*2],qc[maxn],siz[maxn];
int fi1[maxn*3],ne1[maxn*5],dui1[maxn*5],qc1[maxn*3];
int fa[maxn],dfn[maxn],top[maxn],go[maxn],root[maxn*10];
bool bq;
int i,j,k,l,m,n,x,y,z,o,ans,now,q,xz,zo,zz,cz,mx,xx,yy,p,xc;
char s[maxn];

void add(int x,int y){
    if (fi[x]==0) fi[x]=++mx; else ne[qc[x]]=++mx;
    dui[mx]=y; qc[x]=mx;
}
void add1(int x,int y){
    if (fi1[x]==0) fi1[x]=++mx; else ne1[qc1[x]]=++mx;
    dui1[mx]=y; qc1[x]=mx;
}
void ge_a(){
    k=0;
    fo(i,1,26) if (tree[0][i]) h[++k]=tree[0][i];
    i=k; j=0;
    while (i>j){
        j++; x=h[j];
        fo(k,1,26) if (tree[x][k]){
            now=fail[x];
            while (tree[now][k]==0 && now>0) now=fail[now];
            if (tree[now][k]) now=tree[now][k];
            fail[tree[x][k]]=now;
            h[++i]=tree[x][k];
        }
    }
}
void ge_ff(){
    fo(j,1,16)
        fo(i,1,n) ff[i][j]=ff[ff[i][j-1]][j-1];
}
int lca(int x,int y){
    if (de[x]<de[y]){
        int z=x; x=y; y=z;
    }
    fo1(i,16,0) if (de[x]-(1<<i)>=de[y]) x=ff[x][i];
    fo1(i,16,0) if (ff[x][i]!=ff[y][i]){
        x=ff[x][i]; y=ff[y][i];
    }
    if (x!=y) x=ff[x][0];
    return x;
}
void dfs(int x){
    int i=fi[x]; siz[x]=1; 
    while (i){
        de[dui[i]]=de[x]+1;
        dfs(dui[i]);
        siz[x]+=siz[dui[i]];
        if (siz[dui[i]]>siz[go[x]]) go[x]=dui[i];
        i=ne[i];
    }
}
void dfss(int x){
    dfn[x]=++now;
    top[go[x]]=top[x];
    if (go[x]) dfss(go[x]);
    int i=fi[x];
    while (i){
        if (dui[i]==go[x]){
            i=ne[i]; continue;
        }
        top[dui[i]]=dui[i];
        dfss(dui[i]);
        i=ne[i];
    }
}
void maketree(int v,int s1,int s2,int l,int r){
    tr[++p][0]=tr[v][0]; tr[p][1]=tr[v][1]; tr[p][2]=tr[v][2];
    int q=p;
    if (l==r){
        tr[p][2]=s2; return;
    }
    int mid=(l+r)/2;
    if (s1<=mid){
        tr[p][0]=p+1;
        maketree(tr[v][0],s1,s2,l,mid);
    } else{
        tr[p][1]=p+1;
        maketree(tr[v][1],s1,s2,mid+1,r);
    }
    tr[q][2]=max(tr[tr[q][0]][2],tr[tr[q][1]][2]);
}
void wor(int x){
    if (! root[fail[x]] && fail[x]) wor(fail[x]);
    if (fi1[x]){
        int pp=fi1[x];
        root[x]=p+1;
        maketree(root[fail[x]],dfn[dui1[pp]],dep[x],1,n); pp=ne1[pp];
        while (pp){
            int qq=p+1;
            maketree(root[x],dfn[dui1[pp]],dep[x],1,n);
            root[x]=qq;
            pp=ne1[pp];
        }
    }
        else root[x]=root[fail[x]];
}
void find(int v,int l,int r,int x,int y){
    if (l==x && r==y) ans=max(ans,tr[v][2]); else{
        int mid=(l+r)/2;
        if (y<=mid) find(tr[v][0],l,mid,x,y); else
        if (x>mid) find(tr[v][1],mid+1,r,x,y); else{
            find(tr[v][0],l,mid,x,mid);
            find(tr[v][1],mid+1,r,mid+1,y);
        }
    }
}
void tur(int q){
    xx=x; yy=y;
    while (top[xx]!=top[yy]){
        if (de[top[xx]]<de[top[yy]]){
            find(root[q],1,n,dfn[top[yy]],dfn[yy]);
            yy=fa[top[yy]];
        } else{
            find(root[q],1,n,dfn[top[xx]],dfn[xx]);
            xx=fa[top[xx]];
        }
    }
    if (dfn[xx]>dfn[yy]){
        z=xx; xx=yy; yy=z;
    }
    find(root[q],1,n,dfn[xx],dfn[yy]);
}
int main(){
//  freopen("t2.in","r",stdin);
//  freopen("t2.out","w",stdout);
    scanf("%d%d",&n,&o);
    fo(i,1,n){
        scanf("%s",s+1); l=strlen(s+1);
        now=0;
        fo(j,1,l){
            x=s[j]-96;
            if (tree[now][x]) now=tree[now][x]; else{
                tree[now][x]=zo+1; dep[++zo]=dep[now]+1; 
                now=tree[now][x];
            }
        }
        add1(now,i);
    }
    ge_a(); mx=0;
    fo(i,2,n){
        scanf("%d",&ff[i][0]); fa[i]=ff[i][0];
        add(ff[i][0],i);
    }
    ge_ff();
    now=0;
    dfs(1); top[1]=1; 
    dfss(1); p=0;
    fo(i,1,zo)
        if (! root[i]) wor(i);
    scanf("%d",&q); ans=0;
    fo(xz,1,q){
        scanf("%d%d",&x,&y);
        if (o){
            x^=ans; y^=ans;
        } xx=x; yy=y;
        z=lca(x,y);
        now=0; scanf("%s",s+1); l=strlen(s+1); ans=0;
        fo(i,1,l){
            xc=s[i]-96;
            while (now>0 && tree[now][xc]==0) now=fail[now];
            if (tree[now][xc]) now=tree[now][xc];
            tur(now);
        }
        printf("%d\n",ans);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值