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;
}

cdq分治,带修改二维数点,把时间轴当作一维即静态三维数点,cdq分治+树状数组

#include<iostream>
#include<algorithm>
using namespace std;
const int N=200010;
char s[N];
int n,m,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;
// 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 ans[N];
struct node
{
    int op;
    int a,b,c,cnt;
    int sign,id;
}q[N*5];
int st[N];
bool cmpb(const node &x,const node &y)
{
    return x.b<y.b||x.b==y.b&&x.op<y.op;
}
int fw[N];
int lowbit(int x){return x&-x;}
void update(int k,int x){for(;k<=timestamp;k+=lowbit(k)) fw[k]+=x;}
int query(int k){int res=0;for(;k;k-=lowbit(k)) res+=fw[k];return res;}
void solve(int l,int r)
{
    if(l>=r) return;
    int mid=l+r>>1;
    solve(l,mid),solve(mid+1,r);
    int i=l;
    for(int j=mid+1;j<=r;j++)
    {
        if(q[j].op==1) continue;
        while(i<=mid&&q[i].b<=q[j].b)
        {
            if(q[i].op==1) update(q[i].c,q[i].cnt);
            i++;
        }
        ans[q[j].id]+=q[j].sign*query(q[j].c);
    }
    while(i>l)
    {
        i--;
        if(q[i].op==1) 
            update(q[i].c,-q[i].cnt);
    }
    inplace_merge(q+l,q+mid+1,q+r+1,cmpb);
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>s;
        pos[i]=Trie.insert(s);
    }
    dfs(0);
    int cnt=0;
    for(int i=1;i<=n;i++)
        q[++cnt]={1,0,i,dfn[pos[i]],1};
    
    for(int i=1;i<=m;i++)
    {
        int op;
        cin>>op;
        if(op==1)
        {
            int u,v;
            cin>>u>>v;
            q[++cnt]={1,i,u,dfn[pos[u]],-1};
            q[++cnt]={1,i,v,dfn[pos[v]],-1};
            q[++cnt]={1,i,u,dfn[pos[v]],+1};
            q[++cnt]={1,i,v,dfn[pos[u]],+1};
            swap(pos[u],pos[v]);
        }
        else
        {
            st[i]=1;
            int k,l,r;
            cin>>s;
            cin>>k>>l>>r;
            int u=Trie.find(s,k);
            if(u==-1) ans[i]=0;
            else
            {
                int L=dfn[u],R=dfn[u]+sz[u]-1;
                q[++cnt]={2,i,r,R,0,1,i};
                q[++cnt]={2,i,l-1,R,0,-1,i};
                q[++cnt]={2,i,r,L-1,0,-1,i};
                q[++cnt]={2,i,l-1,L-1,0,1,i};
            }
        }
    }
    solve(1,cnt);
    
    for(int i=1;i<=m;i++)
        if(st[i]) cout<<ans[i]<<'\n';
    return 0;
}

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

这段代码的优化可以从以下几个方面入手: 1. 使用 LINQ 语句来优化 foreach 循环 2. 使用 try catch 块来处理插入数据库时的异常情况 3. 将重复的代码封装成方法来优化代码的复用性 经过优化后的代码如下所示: ``` public void StopShooting() { // 使用 LINQ 语句计算总成绩、脱靶数、击中数、平均成绩、分数等信息 var scoreList = allScore.Select(item => new Score { id = item.Key.ToString(), createTime = item.Value[0].startTime, totalScore = item.Value.Sum(x => x.hitNum).ToString(), hitNum = item.Value.Count(x => x.hitFlag == 1).ToString(), missNum = item.Value.Count(x => x.hitFlag == 0).ToString(), guns = item.Value.Count.ToString(), turnType = item.Value[0].shootingMode.ToString(), turnTheme = item.Value[0].baJiType, playerName = PersonDao.Instance.FindNameByID(item.Key.ToString()).namee, workPlace = item.Value[0].workPlace, grading = GetGrading(item.Value.Average(x => x.hitNum)) }).ToList(); // 将数据添加到数据库中,并处理异常情况 try { ScoreInfoSqlMgr.Instance.Add(scoreList); } catch (Exception ex) { Console.WriteLine("插入数据库失败:" + ex.Message); } // 停止射击,清空所有成绩,存储顺,不再接受射击成绩 allScore.Clear(); dicScore.Clear(); isRecive = false; } // 获取射击分数等级 public string GetGrading(double score) { if (score >= 90) return "优秀"; else if (score >= 80) return "良好"; else if (score >= 60) return "及格"; else return "不及格"; } // 定义成绩类 public class Score { public string id { get; set; } public DateTime createTime { get; set; } public string totalScore { get; set; } public string hitNum { get; set; } public string missNum { get; set; } public string grading { get; set; } public string guns { get; set; } public string turnType { get; set; } public string turnTheme { get; set; } public string playerName { get; set; } public string workPlace { get; set; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值