HDU - 5469,点分治,hash,先处理子树

Antonidas

https://vjudge.net/problem/HDU-5469
Given a tree with N vertices and N−1 edges. Each vertex has a single letter Ci. Given a string S, you are to choose two vertices A and B, and make sure the letters catenated on the shortest path from A to B is exactly S. Now, would you mind telling me whether the path exists?

题意:给你一颗有n个节点的树,每个节点有一个字母c,给你一个字符串s,问是否存在一条路径使得上面的节点字母相连后等于s

思路:首先将字符串s的前缀和后缀hash一下,然后点分治时首先处理出子树所有节点到重心路径的hash值以及对应的字符串长度dep,如果等于s对应的前缀hash值,那么看s相应后缀的hash值是否可由其他子树上的路径得到,可以的话就匹配成功,等于s的后缀hash值时同,最后再标记下s的哪些前缀后缀hash值可以被该子树的路径连接出来,注意需要处理一下s长度等于0的情况

#include<bits/stdc++.h>
#define MAXN 10010
#define P 131
#define ull unsigned long long
using namespace std;
const int INF = 0x3f3f3f3f;
char val[MAXN],s[MAXN];//val为节点字母串,s为待匹配串
int len;//len为待匹配串的长度
int n;
ull pw[MAXN],pre[MAXN],sub[MAXN];//记录幂值,s前缀hash值,s后缀hash值
int tot,head[MAXN];
struct edge
{
    int v,nxt;
}edg[MAXN << 1];
inline void addedg(int u,int v)
{
    edg[tot].v = v;
    edg[tot].nxt = head[u];
    head[u] = tot++;
}
int root,ms,sz[MAXN],Size;
bool vis[MAXN];
void getroot(int u,int f)//获得重心
{
    sz[u] = 1;
    int v,mson = 0;
    for(int i = head[u];i != -1;i = edg[i].nxt)
    {
        v = edg[i].v;
        if(vis[v] || v == f) continue;//剔除已经被分治过的点
        getroot(v,u);
        sz[u] += sz[v];
        if(sz[v] > mson) mson = sz[v];
    }
    if(Size - sz[u] > mson) mson = Size-sz[u];//把u看作根节点时u的父亲那一部分也算作子树
    if(ms > mson) ms = mson,root = u;//更新重心
}
struct node
{
    ull hs;//dis链为从节点往重心的hash值
    int dep;//dep为深度
}dis[MAXN];
int cnt;
bool ans;//记录答案
bool vispre[MAXN],vissub[MAXN];//记录s某个长度的前缀和后缀是否可以得到
int idpre[MAXN],idsub[MAXN],cpre,csub;//记录哪些长度被标记了,方便重置vispre,vissub
inline void getdis(int u,int f,ull d,int dep)
{
    ull w = d+(val[u]-'a'+1)*pw[dep];
    dis[++cnt] = node{w,dep+1};
    int v;
    for(int i = head[u];i != -1;i = edg[i].nxt)
    {
        v = edg[i].v;
        if(v == f || vis[v])
            continue;
        getdis(v,u,w,dep+1);
    }
}
inline void solve(int u,int ssize)
{
    vis[u] = 1;
    cpre = csub = 0;
    if(val[u]-'a'+1 == pre[1] && !vispre[1])
        vispre[1] = true,idpre[++cpre] = 1;
    if(val[u]-'a'+1 == sub[len] && !vissub[len])
    {
        if(vispre[len])
            ans = true;
        vissub[len] = true,idsub[++csub] = len;
    }
    int v;
    for(int i = head[u];i != -1;i = edg[i].nxt)
    {
        v = edg[i].v;
        if(vis[v]) continue;
        cnt = 0;
        getdis(v,u,val[u]-'a'+1,1);
        for(int j = 1;j <= cnt;++j)
        {
            if(dis[j].hs == pre[dis[j].dep] && vissub[dis[j].dep])
                ans = true;
            if(dis[j].hs == sub[len-dis[j].dep+1] && vispre[len-dis[j].dep+1])
                ans = true;
        }
        for(int j = 1;j <= cnt;++j)
        {
            if(dis[j].hs == pre[dis[j].dep] && !vispre[dis[j].dep])
                vispre[dis[j].dep]= true,idpre[++cpre] = dis[j].dep;
            if(dis[j].hs == sub[len-dis[j].dep+1] && !vissub[len-dis[j].dep+1])
                vissub[len-dis[j].dep+1] = true,idsub[++csub] = len-dis[j].dep+1;
        }
    }
    for(int i = 1;i <= cpre;++i)
        vispre[idpre[i]] = false;
    for(int i = 1;i <= csub;++i)
        vissub[idsub[i]] = false;
    for(int i = head[u];i != -1;i = edg[i].nxt)
    {
        v = edg[i].v;
        if(vis[v]) continue;
        ms = INF;
        Size = sz[v] < sz[u] ? sz[v]:(ssize-sz[u]);
        getroot(v,v);
        solve(root,Size);
    }
}
inline void init()
{
    ms = INF,Size = n;
    memset(vis,false,sizeof(bool)*(n+1));
    memset(head,-1,sizeof(int)*(n+1));
    tot = 0;
    ans = false;
}
inline void inithash()
{
    pw[0] = 1,pre[0] = sub[len+1] = 0;
    for(int i = 1;i <= len;++i)
    {
        pw[i] = pw[i-1] * P;
        pre[i] = pre[i-1] * P + s[i] - 'a' + 1;
        sub[len-i+1] = sub[len-i+2] * P + s[len-i+1] - 'a' + 1;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int cas = 1;cas <= t;++cas)
    {
        scanf("%d",&n);
        init();
        int u,v;
        for(int i = 1;i < n;++i)
        {
            scanf("%d%d",&u,&v);
            addedg(u,v),addedg(v,u);
        }
        scanf("%s%s",val+1,s+1);
        len = strlen(s+1);
        inithash();
        getroot(1,1);
        solve(root,Size);
        printf("Case #%d: ",cas);
        if(ans)
            printf("Find\n");
        else
            printf("Impossible\n");
    }
    return 0;
}
/*
5
2
1 2
ab
a

 10
 1 3
 1 6
 1 8
 3 2
 3 4
 3 5
 6 7
 8 9
 9 10
 acbdefffeg
 fafeg
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值