zoukankan      html  css  js  c++  java
  • P3294 [SCOI2016]背单词

    传送门

    贪心+字典树

    很显然,填一个单词前肯定要优先把所有是它后缀的单词填掉

    考虑怎么判后缀

    可以把单词倒过来,加入字典树

    那么填一个单词前要先填掉它所有祖先的单词

    即要从深度小的填到深度大的

    可以按搜索顺序填

    考虑要怎样确定搜索顺序

    随便画一颗树

    发现深度优先比广度优先更优

    在深度优先时 单词少的子树优先更优

    所以可以把每个节点所有的儿子按子树大小排一遍序

    然后深搜,更新答案

    估了一波复杂度好像可以接受

    WA了6个点...

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    const int N=510007;
    int n,tot,len;
    long long ans;
    char s[N];
    inline void read_s()
    {
        len=0;
        char c=getchar();
        while(c<'a'||c>'z') c=getchar();
        while(c>='a'&&c<='z')
        {
            s[len++]=c;
            c=getchar();
        }
    }
    struct node
    {
        int sz,id;
    }nex[N][27];
    inline bool cmp(const node &a,const node &b){ return a.sz<b.sz; }
    int ch[N][27],cnt,sz[N];//sz是每个节点的子树大小
    bool pd[N];//结束标记
    int dfs1(int x)
    //第一遍深搜处理搜索顺序
    {
        for(int i=1;i<=26;i++)
        {
            if(!ch[x][i]) continue;
            nex[x][i].sz=dfs1(ch[x][i]);//遍历儿子
            nex[x][i].id=i;
            sz[x]+=nex[x][i].sz;//更新sz
        }
        sort(nex[x]+1,nex[x]+27,cmp);//对儿子按sz排序
        return sz[x];
    }
    void dfs2(int x,int las)
    //las是最近的有结束标记的祖先
    {
        if(pd[x])//如果有结束标记
        {
            tot++;
            ans+=(tot-las);//更新答案
            las=tot;
        }
        for(int i=1;i<=26;i++)
        {
            int v=nex[x][i].id;
            if(!ch[x][v]) continue;
            dfs2(ch[x][v],las);//搜下去
        }
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            read_s();
            int u=0;
            for(int j=len-1;j>=0;j--)
            {
                int v=s[j]-'a'+1;
                if(!ch[u][v]) ch[u][v]=++cnt;
                u=ch[u][v];
                if(!j) pd[u]=sz[u]=1;
            }//建立字典树
        }
        dfs1(0);
    
        dfs2(0,0);
        cout<<ans;
        return 0;
    }
    WAWAWA

    调试半天发现是自己算法的问题...

    如果是这样一颗树:

    那么我深搜的顺序就是先向右边两个跑

    右边跑完了再往左边跑

    但是应该先往最左边的一个跑....

    解决方法很简单

    把中间没用的白色节点去掉

    把根和蓝色节点的关系拿出来重新建一颗树

    再按子树大小一个个深搜

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    using namespace std;
    const int N=510007;
    int n,tot,len;
    long long ans;
    char s[N];
    inline void read_s()
    {
        len=0;
        char c=getchar();
        while(c<'a'||c>'z') c=getchar();
        while(c>='a'&&c<='z')
        {
            s[len++]=c;
            c=getchar();
        }
    }
    int ch[N][27],cnt,sz[N],f[N];
    bool pd[N];
    inline bool cmp(const int &a,const int &b){ return sz[a]<sz[b]; }
    vector <int> v[N];//新图用vector存很方便
    void rebuild(int x,int fx)//重建
    {
        int t= pd[x] ? x : fx;
        for(int i=1;i<=26;i++)
        {
            int u=ch[x][i]; if(!u) continue;
            rebuild(u,t);
        }
        if(pd[x])
            v[fx].push_back(x);
    }
    void prework(int x)//对儿子的顺序排序
    {
        int l=v[x].size();
        for(int i=0;i<l;i++)
        {
            int u=v[x][i];
            prework(u);
            sz[x]+=sz[u];
        }
        sort(v[x].begin(),v[x].end(),cmp);//骚操作
    }
    void dfs(int x,int fa)//深搜更新答案
    {
        if(x) f[x]=++tot;
        ans+=(f[x]-f[fa]);
        int l=v[x].size();
        for(int i=0;i<l;i++)
            dfs(v[x][i],x);
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            read_s();
            int u=0;
            for(int j=len-1;j>=0;j--)
            {
                int x=s[j]-'a'+1;
                if(!ch[u][x]) ch[u][x]=++cnt;
                u=ch[u][x];
                if(!j) pd[u]=sz[u]=1;
            }//建字典树
        }
    
        rebuild(0,0);
    
        prework(0);
    
        dfs(0,0);
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    WingIIDE的licese破解方法
    HttpCookie
    Jquery中html()方法 and "click"绑定后代元素
    jquery 选择器多个
    input标签的type为select、radio、checkbox的使用
    c#中?和??使用
    VS中使用附加进程来调试项目
    刷新局部页面
    ant design vue 文件上传的集中页面
    vue 上传超大文件出现Uncaught (in promise) Error: Network Error at createError
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9655097.html
Copyright © 2011-2022 走看看