HDU 4601 Letter Tree

人生第一次300行代码,呕心沥血啊。。果断留帖纪念下


debug一天,结果是线段树没有初始化。。

对代码的每一个部分如果没有仔细检查过,就不应该有盲目的自信。T_T


思路还是写一下好了,虽然不是自己的东西。。

节点数有10W,询问数有10W,显然对于每一个节点都搜一次必然要超时。注意到每次询问的终点都在同一层,那么是不是可以通过维护每一层的最值来达到快速询问的目的呢。

我们可以按照DFS的顺序对节点进行编号,那么一个节点的子节点的编号必然都在这个节点和这个节点的兄弟节点之间,通过这个性质,我们再对每个节点按层数排序,同一层的节点按照搜索的时间戳排序,那么我们就可以通过几次二分索引到某个节点在某一层的所有子节点,然后选一种数据结构维护一下最大值就可以了,线段树啊,RMQ啊都可以,然后剩下的就是敲啊敲代码。注意一下这里的最大值指的是对应节点在TRIE上的排名rank,因为值被取模的关系,按值的大小排必然出错。

本题爆栈率极高,代码第一行为扩栈用- -!


#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
typedef __int64 lld;
#define clr(a,b) memset(a,b,sizeof(a))
#define DEBUG printf("nani!\n");
const lld INF = ~0u>>1;
const lld MOD = 1e9+7;
const lld MAXN = 200100;

lld n,tk;
lld dd[MAXN] = {1};

// 节点的信息
struct POINT{
    lld id,time,lev,val,rank;
    lld netime;
    bool operator < (const POINT &tt) const
    {
        if(lev == tt.lev)
        {
            return time < tt.time;
        }
        return lev < tt.lev;
    }
}point[MAXN];

// 线段树
lld rgl[MAXN], rgr[MAXN];
lld M;
POINT t[MAXN<<2];


// 临接表
struct E{
    lld v,w,next;
}edge[MAXN<<1];
lld head[MAXN],edgetot;

// trie
struct NODE{
    lld c[26];
    lld rank;
}nod[MAXN];
lld nodetot;

void add_edge(lld u, lld v, lld w)
{
    edge[edgetot].next = head[u];
    edge[edgetot].v = v;
    edge[edgetot].w = w;
    head[u] = edgetot ++;
}

lld New_node()
{
    clr(nod[nodetot].c,-1);
    return nodetot ++;
}

void dfs1(lld u, lld fa, lld node, lld Lev,lld Val)
{
    point[u].time = ++tk;
    point[u].lev = Lev;
    point[u].val = Val;
    point[u].id = u;
    lld p;
    for(p=head[u]; p!=-1; p=edge[p].next)
    {
        lld v = edge[p].v;
        lld w = edge[p].w;
        if(v == fa) continue;
        if(nod[node].c[w] == -1) nod[node].c[w] = New_node();
        dfs1(v,u,nod[node].c[w],Lev+1,(Val*26%MOD+w)%MOD);
    }
}

void tdfs(lld u)
{
    nod[u].rank = ++tk;
    lld i;
    for(i=0; i<26; i++)
    {
        if(nod[u].c[i] != -1) tdfs(nod[u].c[i]);
    }
}

void dfs2(lld u, lld fa, lld node)
{
    point[u].rank = nod[node].rank;
    lld p;
    for(p=head[u]; p!=-1; p=edge[p].next)
    {
        lld v = edge[p].v;
        lld w = edge[p].w;
        if(v == fa) continue;
        dfs2(v,u,nod[node].c[w]);
    }
}

bool cmp(POINT a, POINT b)
{
    return a.id < b.id;
}

void build_sgt()
{
    lld i;
    for(i=M; i>=1; i--)
    {
        t[i].rank = -1;
        if(t[i].rank < t[i<<1].rank)
        {
            t[i].rank = t[i<<1].rank;
            t[i].id = t[i<<1].id;
        }
        if(t[i].rank < t[i<<1|1].rank)
        {
            t[i].rank = t[i<<1|1].rank;
            t[i].id = t[i<<1|1].id;
        }
    }
}

lld query(lld l, lld r)
{
    l += -1;
    r += 1;
    POINT ret;
    ret.rank = -1;
    ret.id = -1;
    for(; l^r^1; l>>=1, r>>=1)
    {
        if(~l&1)
        {
            if(t[l^1].rank > ret.rank)
            {
                ret.rank = t[l^1].rank;
                ret.id = t[l^1].id;
            }
        }
        if(r&1)
        {
            if(t[r^1].rank > ret.rank)
            {
                ret.rank = t[r^1].rank;
                ret.id = t[r^1].id;
            }
        }
    }
    return ret.id;
}

void gao()
{
    tk = 0;
    dfs1(1,-1,0,1,0);
    tk = 0;
    tdfs(0);
    dfs2(1,-1,0);
    for(M=1; M<n+2; M<<=1);

    sort(point+1,point+n+1);
    lld i;
    rgl[1] = rgr[1] = 1+M;
    tk = 1;
    for(i=2; i<=n; i++)
    {
        if(point[i].lev != point[i-1].lev)
        {
            tk ++;
            rgl[tk] = rgr[tk] = i+M;
        }
        else
        {
            rgr[tk] = i+M;
        }
    }
    point[n+1].lev = INF;
    for(i=1; i<=n; i++)
    {
        t[i+M] = point[i];
        if(point[i].lev == point[i+1].lev)
        {
            point[i].netime = point[i+1].time;
        }
        else
        {
            point[i].netime = n+1;
        }
    }
    sort(point+1,point+n+1,cmp);

    build_sgt();
}

void init()
{
    edgetot = 0;
    nodetot = 0;
    clr(head, -1);
    New_node();
}

lld Find_l(lld l, lld r,lld ss)
{
    while(r-l>1)
    {
        lld m = (l+r)>>1;
        if(t[m].time >= ss) r = m;
        else l = m+1;
    }
    if(t[l].time >= ss) return l;
    else if(t[r].time >= ss) return r;
    else return -1;
}

lld Find_r(lld l, lld r, lld ss)
{
    while(r-l>1)
    {
        lld m = (l+r)>>1;
        if(t[m].time <= ss) l = m;
        else r = m;
    }
    if(t[r].time <= ss) return r;
    else if(t[l].time <= ss) return l;
    else return -1;
}

lld Solve(lld u, lld step)
{
    lld Lev = point[u].lev + step;
    if(step > tk) return 0;
//    printf("Lev = %I64d\n", Lev);
    if(Lev > tk) return 0;
    lld l,r;
    l = rgl[Lev];
    r = rgr[Lev];
    lld L = point[u].time;
    lld R = point[u].netime - 1;
    l = Find_l(l,r,L);
    if(l <= M || l>r || l>=n+M+1) return 0;
    r = Find_r(l,r,R);
    if(r <=M || l>r || r>=n+M+1) return 0;
    lld id = query(l,r);
//    printf("id = %I64d\n", id);
    lld ans = ((point[id].val - dd[step]*point[u].val%MOD)%MOD+MOD)%MOD;
    printf("%I64d\n", ans);
    return 1;
}


void debug()
{
    lld i;
    for(i=1;i<=n; i++)
    {
        printf("id = %I64d, rank = %I64d, pos = %I64d\n", t[i+M].id, t[i+M].rank, i+M);
    }

    printf("M = %I64d\n", M);
    for(i=1; i<=tk; i++) printf("rgl = %I64d, rgr = %I64d\n", rgl[i],rgr[i]);

    for(i=1; i<=n; i++)
    {
        printf("id = %I64d, rank = %I64d, time = %I64d, val = %I64d, lev = %I64d\n", point[i].id,point[i].rank,point[i].time,point[i].val,point[i].lev);
    }
    DEBUG
}

int main()
{
    lld cas;
    lld a,b,i;
    char s[5];
//    freopen("1002.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
    for(i=1; i<MAXN; i++) dd[i] = dd[i-1]*26%MOD;
    scanf("%I64d", &cas);
    while(cas--)
    {
        init();
        scanf("%I64d", &n);
        for(i=0; i<n-1; i++)
        {
            scanf("%I64d%I64d%s", &a, &b, s);
            add_edge(a,b,s[0]-'a');
            add_edge(b,a,s[0]-'a');
        }

        gao();
    //    debug();
        lld q;
        scanf("%I64d", &q);
        while(q--)
        {
            scanf("%I64d%I64d", &a, &b);
            if(Solve(a,b) == 0) printf("IMPOSSIBLE\n");
        }
    }
    return 0;
}

重写了一次,省略了一次DFS,跑得快了一点点,也没有爆栈问题了。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;

typedef __int64 lld;
const int INF = ~0u>>1;
const lld MOD = 1e9+7;
#define clr(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a; i<(b); i++)
#define FOR(i,a,b) for(int i=(a); i<=(b); i++)
#define FORP(i,a,b) for(int i=(a); i>=(b); i--)
#define PB push_back
typedef pair<int , int > P;
int max(int a, int b) { return a>b?a:b; }
int min(int a, int b) { return a<b?a:b; }


const int MAXN = 201000;

int n,rk,Lev;
lld dd[MAXN] = {1};

// 临界表
struct E{
    int v,w,next;
    E(){}
    E(int v, int w, int next) : v(v), w(w), next(next) {}
}edge[MAXN<<1];
int head[MAXN];
int edgetot;

// trie
struct NODE{
    int c[26];
    int rank;
    lld num;
}nod[MAXN];
int nodetot;

struct POINT{
    int time,netime;
    int val,lev,id;
    bool operator < (const POINT &tt) const
    {
        if(lev == tt.lev)
        {
            return time < tt.time;
        }
        return lev < tt.lev;
    }
}point[MAXN];

inline bool cmp(POINT a, POINT b) { return a.id < b.id; }

// 线段树
POINT t[MAXN<<2];
int lp[MAXN],rp[MAXN],pp;
int M;

inline int New_node()
{
    clr(nod[nodetot].c, -1);
    return nodetot ++;
}

void init()
{
    nodetot = edgetot = 0;
    pp = 0;
    clr(head, -1);
    New_node();
    nod[0].rank = 0;
    nod[0].num = 0;
}

void build_sgt()
{
    FORP(i,M,1)
    {
        t[i].val = 0;
        if(nod[t[i].val].rank < nod[t[i<<1].val].rank)
        {
            t[i].val = t[i<<1].val;
        }
        if(nod[t[i].val].rank < nod[t[i<<1|1].val].rank)
        {
            t[i].val = t[i<<1|1].val;
        }
    //    printf("sgt : i = %d, val = %d, rank = %d, num = %I64d\n", i,t[i].val,nod[t[i].val].rank, nod[t[i].val].num);
    }
}

void dfs1(int u, int fa)
{
    point[u].id = u;
    point[u].lev = ++Lev;
    point[u].time = ++rk;
    for(int p = head[u]; p!=-1; p=edge[p].next)
    {
        int v = edge[p].v;
        int w = edge[p].w;
        if(v == fa) continue;
        if(nod[point[u].val].c[w] < 0) nod[point[u].val].c[w] = New_node();
        point[v].val = nod[point[u].val].c[w];
        dfs1(v,u);
    }
    Lev --;
}

void dfs2(int u,lld num)
{
    nod[u].rank = ++rk;
    nod[u].num = num;
//    printf("u = %d, rank = %d, num = %I64d\n", u,nod[u].rank,nod[u].num);
    REP(i,0,26)
    {
        if(nod[u].c[i] >= 0)
        {
            dfs2(nod[u].c[i],(num*26%MOD+i)%MOD);
        }
    }
}

lld query(int l, int r)
{
    l -= 1;
    r += 1;
    int ans = 0;
    for(; l^r^1; l>>=1, r>>=1)
    {
//        puts("what");
        if(~l&1)
        {
//            printf("~~%d\n",nod[t[l^1].val].rank);
            if(nod[ans].rank < nod[t[l^1].val].rank) ans = t[l^1].val;
        }
        if(r&1)
        {
//            printf("__%d\n",nod[t[r^1].val].rank);
            if(nod[ans].rank < nod[t[r^1].val].rank) ans = t[r^1].val;
        }
    }
//    printf("ans = %d\n", ans);

    return nod[ans].num;
}

void gao()
{
    for(M=1; M<n+2; M<<=1);
    rk = 0;
    Lev = 0;
    point[1].val = 0;
    dfs1(1,-1);
    rk = 0;
    dfs2(0,0);

    sort(point+1, point+n+1);
    pp = 0;
    point[n+1].time = INF;
    point[0].lev = 0;
    FOR(i,1,n)
    {
        if(point[i].lev != point[i+1].lev) point[i].netime = n+1;
        else point[i].netime = point[i+1].time;
        if(point[i].lev != point[i-1].lev)
        {
            pp ++;
            lp[pp] = rp[pp] = i+M;
        }
        else rp[pp] = i+M;
        t[i+M] = point[i];
    }

    build_sgt();

    sort(point+1, point+n+1, cmp);
}

int Find_l(int l, int r, int time)
{
    while(l<=r)
    {
        int m = (l+r)>>1;
        if(t[m].time >= time) r = m - 1;
        else l = m + 1;
    }
    return r+1;
}

int Find_r(int l, int r, int time)
{
    while(l<=r)
    {
        int m = (l+r)>>1;
        if(t[m].time >= time) r = m - 1;
        else l = m + 1;
    }

    return r;
}

int Solve(int u, int step)
{
    Lev = point[u].lev + step;
    if(Lev > pp) return 0;
    int l = lp[Lev];
    int r = rp[Lev];
    int L = Find_l(l,r,point[u].time);
//    printf("!!%d %d\n", l,r);
//    FOR(i,l,r) printf("~~~%d\n", t[i].time);
//    printf("L = %d\n", L);
    if(L > r || L < l) return 0;
    int R = Find_r(l,r,point[u].netime);
//    printf("R = %d\n", R);
    if(R > r || R < l) return 0;
    if(L>R) return 0;

    printf("%I64d\n", (query(L,R) - nod[point[u].val].num*dd[step]%MOD+MOD)%MOD);
    return 1;
}

void debug()
{
    FOR(i,1,2*M) printf("%d : %d\n", i, nod[t[i].val].rank);
}

int main()
{
    int cas;
    int a,b;
    char s[2];
    REP(i,1,MAXN) dd[i] = dd[i-1] * 26 % MOD;
    scanf("%d", &cas);
    while(cas--)
    {
        init();
        scanf("%d", &n);
        REP(i,0,n-1)
        {
            scanf("%d%d%s", &a, &b, s);
            edge[edgetot] = E(b,s[0]-'a',head[a]);
            head[a] = edgetot ++;
            edge[edgetot] = E(a,s[0]-'a',head[b]);
            head[b] = edgetot ++;
        }

        gao();
    //    debug();

        int q;
        scanf("%d", &q);
        while(q--)
        {
            scanf("%d%d", &a, &b);
            if(Solve(a,b) == 0) printf("IMPOSSIBLE\n");
        }
    }
    return 0;
}

/*

1
12
1 2 a
1 3 b
2 4 c
2 5 c
2 6 d
3 7 e
3 8 f
3 9 e
7 10 g
7 11 h
9 12 i
10



*/

然后下面是用RMQ写的,有点纳闷,笔者写的RMQ竟然跑得比上面的线段树慢了400MS。。


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;

typedef __int64 lld;
const int INF = ~0u>>1;
const lld MOD = 1e9+7;
#define clr(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a; i<(b); i++)
#define FOR(i,a,b) for(int i=(a); i<=(b); i++)
#define FORP(i,a,b) for(int i=(a); i>=(b); i--)
#define PB push_back
typedef pair<int , int > P;
int max(int a, int b) { return a>b?a:b; }
int min(int a, int b) { return a<b?a:b; }


const int MAXN = 201000;

int n,rk,Lev;
lld dd[MAXN] = {1};

// 临界表
struct E{
    int v,w,next;
    E(){}
    E(int v, int w, int next) : v(v), w(w), next(next) {}
}edge[MAXN<<1];
int head[MAXN];
int edgetot;

// trie
struct NODE{
    int c[26];
    int rank;
    lld num;
}nod[MAXN];
int nodetot;

struct POINT{
    int time,netime;
    int val,lev,id;
    bool operator < (const POINT &tt) const
    {
        if(lev == tt.lev)
        {
            return time < tt.time;
        }
        return lev < tt.lev;
    }
}point[MAXN];

inline bool cmp(POINT a, POINT b) { return a.id < b.id; }

// RMQ
POINT t[MAXN][30];
int lp[MAXN],rp[MAXN],pp;
int lg[MAXN];

inline int New_node()
{
    clr(nod[nodetot].c, -1);
    return nodetot ++;
}

void init()
{
    nodetot = edgetot = 0;
    pp = 0;
    clr(head, -1);
    New_node();
    nod[0].rank = 0;
    nod[0].num = 0;
}

void build_RMQ()
{
    int i, j;
    for(j=1; (1<<j) <= n; j++)
    {
        int tt = 1<<(j-1);
        for(i=1; i+tt<=n; i++)
        {
            t[i][j].val = 0;
            if(nod[t[i][j].val].rank < nod[t[i][j-1].val].rank) t[i][j].val = t[i][j-1].val;
            if(nod[t[i][j].val].rank < nod[t[i+tt][j-1].val].rank) t[i][j].val = t[i+tt][j-1].val;
        }
    }

}

void dfs1(int u, int fa)
{
    point[u].id = u;
    point[u].lev = ++Lev;
    point[u].time = ++rk;
    for(int p = head[u]; p!=-1; p=edge[p].next)
    {
        int v = edge[p].v;
        int w = edge[p].w;
        if(v == fa) continue;
        if(nod[point[u].val].c[w] < 0) nod[point[u].val].c[w] = New_node();
        point[v].val = nod[point[u].val].c[w];
        dfs1(v,u);
    }
    Lev --;
}

void dfs2(int u,lld num)
{
    nod[u].rank = ++rk;
    nod[u].num = num;
//    printf("u = %d, rank = %d, num = %I64d\n", u,nod[u].rank,nod[u].num);
    REP(i,0,26)
    {
        if(nod[u].c[i] >= 0)
        {
            dfs2(nod[u].c[i],(num*26%MOD+i)%MOD);
        }
    }
}

lld query(int l, int r)
{
    int k = lg[r-l+1];
 //   return max(d[L][k], d[R-(1<<k)+1][k]);
    int ans = 0;
    if(nod[ans].rank < nod[t[l][k].val].rank) ans = t[l][k].val;
    if(nod[ans].rank < nod[t[r-(1<<k)+1][k].val].rank) ans = t[r-(1<<k)+1][k].val;
    return nod[ans].num;
}

void gao()
{
    rk = 0;
    Lev = 0;
    point[1].val = 0;
    dfs1(1,-1);
    rk = 0;
    dfs2(0,0);

    sort(point+1, point+n+1);
    pp = 0;
    point[n+1].time = INF;
    point[0].lev = 0;
    FOR(i,1,n)
    {
        if(point[i].lev != point[i+1].lev) point[i].netime = n+1;
        else point[i].netime = point[i+1].time;
        if(point[i].lev != point[i-1].lev)
        {
            pp ++;
            lp[pp] = rp[pp] = i;
        }
        else rp[pp] = i;
        t[i][0] = point[i];
    }

    build_RMQ();

    sort(point+1, point+n+1, cmp);
}

int Find_l(int l, int r, int time)
{
    while(l<=r)
    {
        int m = (l+r)>>1;
        if(t[m][0].time >= time) r = m - 1;
        else l = m + 1;
    }
    return r+1;
}

int Find_r(int l, int r, int time)
{
    while(l<=r)
    {
        int m = (l+r)>>1;
        if(t[m][0].time >= time) r = m - 1;
        else l = m + 1;
    }

    return r;
}

int Solve(int u, int step)
{
    Lev = point[u].lev + step;
    if(Lev > pp) return 0;
    int l = lp[Lev];
    int r = rp[Lev];
    int L = Find_l(l,r,point[u].time);
//    printf("!!%d %d\n", l,r);
//    FOR(i,l,r) printf("~~~%d\n", t[i].time);
//    printf("L = %d\n", L);
    if(L > r || L < l) return 0;
    int R = Find_r(l,r,point[u].netime);
//    printf("R = %d\n", R);
    if(R > r || R < l) return 0;
    if(L>R) return 0;

    printf("%I64d\n", (query(L,R) - nod[point[u].val].num*dd[step]%MOD+MOD)%MOD);
    return 1;
}


int main()
{
    int cas;
    int a,b;
    char s[2];
    REP(i,1,MAXN) dd[i] = dd[i-1] * 26 % MOD;
    lg[0] = -1;
    REP(i,1,MAXN) lg[i] = lg[i>>1] + 1;
    scanf("%d", &cas);
    while(cas--)
    {
        init();
        scanf("%d", &n);
        REP(i,0,n-1)
        {
            scanf("%d%d%s", &a, &b, s);
            edge[edgetot] = E(b,s[0]-'a',head[a]);
            head[a] = edgetot ++;
            edge[edgetot] = E(a,s[0]-'a',head[b]);
            head[b] = edgetot ++;
        }

        gao();

        int q;
        scanf("%d", &q);
        while(q--)
        {
            scanf("%d%d", &a, &b);
            if(Solve(a,b) == 0) printf("IMPOSSIBLE\n");
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值