F. Paper Grading(Trie树+dfs序+二维数点)

F. Paper Grading

大佬题解
一般关于前缀的问题基本都是Trie树。

首先将所给字符串建立一棵Trie树,Trie能够解决一个字符串在一个字符串集合中出现的次数,而查询前缀次数只需要找到Trie树中所给字符末尾的位置,那么其子树中打标记的次数即前缀次数。

由于子树dfs序[L,R]连续,于是把字典树按照dfs序标记,即变成区间查询问题[l,r]中[L,R]之间数的个数,二位偏序问题。

树套树(树状数组套下标线段树)即可解决,要动态开点!

#include<iostream>
using namespace std;
const int N=200010;
char s[N];
int n,q,pos[N];
// Trie树
struct T1
{
    
    
    int tree[N][26],idx;
    int insert(char s[])
    {
    
    
        int p=0;
        for(int i=0;s[i];i++)
        {
    
    
            int t=s[i]-'a';
            if(!tree[p][t]) tree[p][t]=++idx;
            p=tree[p][t];
        }
        return p;
    }
    int find(char s[],int k)
    {
    
    
        int p=0;
        for(int i=0;i<k;i++)
        {
    
    
            int t=s[i]-'a';
            if(!tree[p][t]) return -1;
            p=tree[p][t];
        }
        return p;
    }
}Trie;
// 动态开点线段树
struct T2
{
    
    
    struct node
    {
    
    
        int l,r;
        int sz;
    }tree[N*40];
    int root[N],cnt;
    void update(int &u,int l,int r,int pos,int x)
    {
    
    
        if(!u) u=++cnt;
        tree[u].sz+=x;
        if(l==r) return;
        int mid=l+r>>1;
        if(pos<=mid) update(tree[u].l,l,mid,pos,x);
        else update(tree[u].r,mid+1,r,pos,x);
    }
    int query(int u,int l,int r,int L,int R)
    {
    
    
        if(!u) return 0;
        if(L<=l&&r<=R) return tree[u].sz;
        int mid=l+r>>1;
        int v=0;
        if(L<=mid) v+=query(tree[u].l,l,mid,L,R);
        if(R>mid) v+=query(tree[u].r,mid+1,r,L,R);
        return v;
    }
}Segment;
// dfs序转化为区间
int dfn[N],sz[N],timestamp;
void dfs(int u)
{
    
    
    dfn[u]=++timestamp;
    sz[u]=1;
    for(int i=0;i<26;i++)
        if(Trie.tree[u][i]) 
            dfs(Trie.tree[u][i]),sz[u]+=sz[Trie.tree[u][i]];
}
// 树状数组
int lowbit(int x) {
    
    return x&-x;}
void add(int k,int pos,int x)
{
    
    
    for(;k<=n;k+=lowbit(k))
        Segment.update(Segment.root[k],1,timestamp,pos,x);
}
int sum(int k,int L,int R)
{
    
    
    int res=0;
    for(;k;k-=lowbit(k))
        res+=Segment.query(Segment.root[k],1,timestamp,L,R);
    return res;
}
int main()
{
    
    
    cin>>n>>q;
    for(int i=1;i<=n;i++)
    {
    
    
        cin>>s;
        pos[i]=Trie.insert(s);
    }
    dfs(0);
    for(int i=1;i<=n;i++)
        add(i,dfn[pos[i]],1);
    while(q--)
    {
    
    
        int op;
        cin>>op;
        if(op==1)
        {
    
    
            int u,v;
            cin>>u>>v;
            add(u,dfn[pos[u]],-1);
            add(v,dfn[pos[v]],-1);
            add(u,dfn[pos[v]],1);
            add(v,dfn[pos[u]],1);
            swap(pos[u],pos[v]);
        }
        else
        {
    
    
            int k,l,r;
            cin>>s;
            cin>>k>>l>>r;
            int u=Trie.find(s,k);
            if(u==-1) cout<<0<<'\n';
            else
            {
    
    
                int L=dfn[u],R=dfn[u]+sz[u]-1;
                cout<<sum(r,L,R)-sum(l-1,L,R)<<'\n';
            }
        }
    }
    return 0;
}

写代码过程中总是弄不清记得东西,每次都是一层一层的想,尤其是dfs序问题,很迷糊,还是要多写,多积累!!!要不然训练总是挂机

Trie板子

// Trie树
struct T
{
    
    
    int tree[N][26],idx,cnt[N];
    int insert(char s[])
    {
    
    
        int p=0;
        for(int i=0;s[i];i++)
        {
    
    
            int t=s[i]-'a';
            if(!tree[p][t]) tree[p][t]=++idx;
            p=tree[p][t];
        }
        cnt[p]++;
        return p;
    }
    int find(char s[],int k)
    {
    
    
        int p=0;
        for(int i=0;i<k;i++)
        {
    
    
            int t=s[i]-'a';
            if(!tree[p][t]) return -1;
            p=tree[p][t];
        }
        return p;
    }
    int query(char s[])
    {
    
    
        int p=0;
        for(int i=0;s[i];i++)
        {
    
    
            int t=s[i]-'a';
            if(!tree[p][t]) return 0;
            p=tree[p][t];
        }
        return cnt[p];
    }
}Trie;

动态开点线段树

struct T2
{
    
    
    struct node
    {
    
    
        int l,r;
        int sz;
    }tree[N*40];
    int root[N],cnt;
    void update(int &u,int l,int r,int pos,int x)
    {
    
    
        if(!u) u=++cnt;
        tree[u].sz+=x;
        if(l==r) return;
        int mid=l+r>>1;
        if(pos<=mid) update(tree[u].l,l,mid,pos,x);
        else update(tree[u].r,mid+1,r,pos,x);
    }
    int query(int u,int l,int r,int L,int R)
    {
    
    
        if(!u) return 0;
        if(L<=l&&r<=R) return tree[u].sz;
        int mid=l+r>>1;
        int v=0;
        if(L<=mid) v+=query(tree[u].l,l,mid,L,R);
        if(R>mid) v+=query(tree[u].r,mid+1,r,L,R);
        return v;
    }
}Segment;

猜你喜欢

转载自blog.csdn.net/Fighting_Peter/article/details/112498714