JZOJ 4348 打击目标

打击目标

Description

给出一棵大小为 n 的树,第i个点有一个代表 i 的字符串mi
接下来给出 q 组询问,每组询问两个整数s, t 和一个非空 的询问串w
对于每次询问,询问在 s t的路径上的所有的点的代表字符串,为询问串子串的串的最长长度。
强制在线。

Data Constraint

这里写图片描述

Solution

这题有一个很好的性质就,是 mi 小于等于 10 ,也就意味着每次答案最大为 10
对于每个询问串,暴力枚举它的长度不超过 10 的子串,判断子串在 s t的路径上是否出现过就可以了,当然枚举并不会超时。
接下来考虑如何判断某个串在 s t的路径上是否出现过。
每一个点维护一棵 trie ,记录的信息为该点到根节点上所有出现过的代表字符串。
考虑到一个点与其父亲维护的 trie 会享有同样的信息,于是用可持久化 trie 实现便可解决空间问题。
判断的话也很简单,用两个点的 trie 上的信息减去 lca trie 的信息作差便可判断。

Code

#include<iostream>
#include<cstring>
#include<cstdio>

#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b

using namespace std;
typedef long long ll;
const ll N=12e4,M=10*N,K=20;

struct note{
    int num;
    int son[26];
}trie[M];

char m[N][11],xw[N];
int n,j,k,l,i,O,p,S,T,oo;
int fa[N],f[N][26],dep[N],root[N],len[N];

inline int search(int a,int b)
{
    if(dep[a]>dep[b])swap(a,b);
    int l;
    for(l=20;l>=0;--l)if(dep[f[b][l]]>=dep[a])b=f[b][l];
    for(l=20;l>=0;--l)
    if(f[b][l]!=f[a][l])b=f[b][l],a=f[a][l];
    return a!=b?fa[a]:a;
}

inline void merge(int past,int now,int nu,int ws)
{
    trie[now].num=trie[past].num;
    fo(i,0,25)trie[now].son[i]=trie[past].son[i]; 
    if(ws==len[nu])
    {++trie[now].num; return;} 
    int wz=m[nu][ws+1]-97;  trie[now].son[wz]=++oo;  
    merge(trie[past].son[wz],trie[now].son[wz],nu,ws+1);
}

int main()
{
    cin>>n>>O;
    fo(i,1,n)scanf("%s",m[i]+1);
    fo(i,1,n)len[i]=strlen(m[i]+1);
    dep[1]=root[1]=oo=1;
    merge(root[0],root[1],1,0);
    fo(i,2,n){
        scanf("%d",&fa[i]); f[i][0]=fa[i];
        dep[i]=dep[fa[i]]+1; int l=0; 
        for(;f[f[i][l]][l];++l)f[i][l+1]=f[f[i][l]][l];
        root[i]=++oo;
        merge(root[fa[i]],root[i],i,0);
    }
    int lasten=0,q;cin>>q; 
    fo(tt,1,q){
        scanf("%d%d%s",&S,&T,xw+1);
        if(O)S^=lasten,T^=lasten;
        int cd=strlen(xw+1);
        lasten=0;
        fd(i,min(cd,10),1){
            fd(be,cd-i+1,1){
                int a1=root[S],a2=root[T];
                int a3=search(S,T),a4=root[fa[a3]]; a3=root[a3];
                fo(l,be,be+i-1)a1=trie[a1].son[xw[l]-97],a2=trie[a2].son[xw[l]-97];
                fo(l,be,be+i-1)a3=trie[a3].son[xw[l]-97],a4=trie[a4].son[xw[l]-97];
                if(trie[a1].num+trie[a2].num-trie[a3].num-trie[a4].num){
                    lasten=i; break;
                }
            }
            if(lasten)break;
        }
        printf("%d\n",lasten);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值