牛客网 第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛 B题 跳一跳,很简单的。二分长度 字符串哈希 倍增找K代父节点

链接:https://www.nowcoder.com/acm/contest/90/B
来源:牛客网

题目描述

有一无限循环字母表:

现在有一棵n个节点(编号1-n)的有根树(1为根),树边边权为一个字母θ,在每一时刻θ会向前跳K步变为相应字母(即树边边权改变),如:

n每一时刻会向前跳3步,第1时刻变为q,第2时刻变为t,以此类推。

w每一时刻会向前跳2步,第1时刻变为y,第2时刻变为a,以此类推。

JK会给你Q个询问,让你判断两个节点在t时刻到根节点路径权值(路径权值为该节点到根节点的路径上字母按顺序拼成的字符串)的字典序大小关系。
输入描述:

第一行一个整数T ( 0 < T < 3),代表测试数据组数。
每一组测试数据第一行给出树的节点数n(1< n <=100000)。
接下去的n-1行的第i行给出一个整数P(1<=P<=n),一个字母θ([a-z])以及字母变换的步数K(0<=K<=10000),表示编号为i+1的节点的父亲节点编号为P,以及边的描述。(输入保证为一棵树)
下一行询问数Q(0 < Q<=10000),每个询问一行给出整数u(2<=u<=n),v(2<=v<=n),t(0<=t<=10000),判断编号为u,v两个节点在t时刻到根节点路径权值的字典序大小关系。

输出描述:

对每个询问输出一行答案,
编号u到根节点路径权值的字典序小于v的输出“<”,
相等输出“=”,
否则输出“>”。(不包含该引号)

示例1
输入

1
10
1 a 1
1 a 5
1 c 2
2 f 2
3 a 5
3 e 3
4 b 1
5 z 1
7 o 2
4
5 7 0
5 7 2
9 10 1
8 8 8

输出

>
<
<
=

解题思路:
1. 从最暴力的做法开始想起,可以暴力的一个一个往上跳,找到第一个不同的字符,然后进行比较即可。 复杂度O(Qn)。 题目只给了1s的时间 会超时。
2. 但这个字符串是否相同是有单调性的,所以想到可以二分查询两条路径上字符串相同的长度。最多会进行logn次查询,那么每次查询的复杂度就不能是O(n) 也就是说不能暴力的去查找。 开始想如何优化。
3. 由于是字符串 两个字符串是否相等 可以哈希预处理有在O(1)的时间内判断。 所以可以在O(n)的时间内处理处所有字符串前缀的哈希值。
4. 那么现在,假设当前二分查询的长度为len,那么只要知道当前节点往上跳len次的节点是什么了。直接开数组暴力存所有节点肯定不行。 联想到lca的树上倍增。 这样就可以在O(logn)的时间内查找到k代父节点的位置了。所以查询复杂度为O(Q*logn*logn) 因为树有26种状态,所以预处理的复杂度为O(26*n*logn) 。总复杂度达到了可以接受的范围。 不过牛客网的评测机不是很稳定,时间浮动范围可能达到400ms,所以代码常数比较大的话 可以尝试多交几发。。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int MAX=1e5+10;
const ULL seed=652525;
class Edge {
public:
    int u,v,next;
    char w;
};
int head[MAX];
int tot;
Edge edge[MAX<<2];
void add(int u,int v,char w) {
    edge[tot].u=u,edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].next=head[u];
    head[u]=tot;
    tot++;
}
ULL base[MAX];
ULL tree[30][MAX];
int fa[MAX][20];
int k[MAX];
char thew[MAX];
char lists[26];
int depth[MAX];
void build(int u,int per) {
    fa[u][0]=per;
    //cout<<u<<endl;
    for(int i=1; i<20; i++) {
        fa[u][i]=fa[fa[u][i-1]][i-1];
    }
    for(int i=head[u]; i!=-1; i=edge[i].next) {
        build(edge[i].v,u);
    }
}
void dfs1(int id,int u) {
    for(int i=head[u]; i!=-1; i=edge[i].next) {
        int v=edge[i].v;
        depth[v]=depth[u]+1;
        tree[id][v]=tree[id][u]*seed+lists[(edge[i].w-'a'+k[v]*id)%26];
        dfs1(id,v);
    }
}
ULL gethash(int len,int e,int id) {
    int s=e;
    for(int i=0; (1<<i)<=len; i++) {
        if((1<<i)&len) {
            s=fa[s][i];
        }
    }
    return tree[id][e]-tree[id][s]*base[len];
}
bool check(int u,int v,int len,int id) {
    //cout<<len<<","<<id<<","<<gethash(len,u,id)<<","<<gethash(len,v,id)<<endl;
    return gethash(len,u,id)==gethash(len,v,id);
}
int bound(int u,int v,int t) {
    int r=min(depth[u],depth[v]);
    int l=0;
    int ans=-1;
    while(l<=r) {
        int m=(l+r)>>1;
        if(check(u,v,m,t)) {
            l=m+1;
        } else {
            r=m-1;
            ans=m;
        }
    }
    //cout<<ans<<endl;
    if(ans==-1) {
        if(depth[u]==depth[v]) return -1;
        else if(depth[u]>depth[v]) return 1;
        else return 0;
    } else {
        ans--;
        for(int i=0; (1<<i)<=ans; i++) {
            if((1<<i)&ans) {
                u=fa[u][i];
                v=fa[v][i];
            }
        }
        if((thew[u]-'a'+k[u]*t)%26>(thew[v]-'a'+k[v]*t)%26) return 1;
        else return 0;
    }
}
void init() {
    tot=0;
    memset(head,-1,sizeof head);
    memset(tree,0,sizeof tree);
    memset(depth,0,sizeof depth);
    memset(k,0,sizeof k);
}
int main() {
    base[0]=1;
    for(int i=1; i<MAX; i++) {
        base[i]=base[i-1]*seed;
    }
    for(int i=0; i<26; i++) {
        lists[i]=i+'a';
    }
    int T;
    scanf("%d",&T);
    while(T--) {
        int n;
        scanf("%d",&n);
        init();
        for(int i=2; i<=n; i++) {
            int v;
            char str;
            scanf("%d %c %d",&v,&str,&k[i]);
            thew[i]=str;
            add(v,i,str);
        }
        build(1,1);// 倍增父节点
        for(int i=0; i<26; i++) {// 预处理出26种状态
            dfs1(i,1);
        }
        int Q;
        scanf("%d",&Q);
        int u,v,t;
        while(Q--) {
            scanf("%d %d %d",&u,&v,&t);
            t%=26;// 以26位循环节
            int ans=bound(u,v,t);
            if(ans==-1) puts("=");
            else if(ans==1) puts(">");
            else puts("<");
        }
    }
}
/*
1
10
1 a 1
1 a 5
1 c 2
2 f 2
3 a 5
3 e 3
4 b 1
5 z 1
7 o 2
4
5 7 0
5 7 2
9 10 1
8 8 8
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值