• [SCOI2016] 背单词 (Trie 树,贪心)


    题目链接


    大致题意

    给你 (n) 个字符串, 要求你给出最小的代价.
    对于每个字符串:
    1.如果它的后缀在它之后,那么代价为 (n^2).
    2.如果一个字符串没有后缀,那么代价为 (x), (x)是它所处的位置.
    3.如果字符串前面有它的后缀且位置为 (y),那么代价为 (x-y).

    Solution

    比较鬼畜的体面,出题人语文水平亟需提高...

    1. 反向建立一棵 Trie 树.

    2. 根据 Trie 树对于每一个被标记过的点重新连边.

    3. 考虑贪心
      条件1 有它一定不是最优的,显然后面两种贡献最多也才(n) .

      令每一个点为 (rt), 子节点为 (u).(siz)以其为后缀的字符串个数.
      题意很明显要找一种最优的 dfs 序.
      使得 (sum{id[u]-id[rt]}) 最小.
      1.很明显有后缀的我们先走其后缀再走它自己
      2.可以证明每次先走 (siz) 小的点总是最优的.

      简单证明:
      令现在有一个点为(x),且其有两个子节点(u_1,u_2),(siz)分别为(siz_1,siz_2).
      且满足 (siz_1>siz_2).
      如果先走(1)节点,那么代价为:
      (id[x]+siz[u_1]*2+siz[u_2])
      这个式子,可以自己根据题意自己理解一下.
      因为很明显我们要先走完(1)才能走(2).
      同理先走(2)节点代价为 (id[x]+siz[u_2]*2+siz[u_1]).
      很明显 先走 (2) 代价小于先走 (1).


    ### Code ```cpp #include #define ll long long using namespace std; const int maxn=510008; const int maxm=100008; int tot,n,size; int pd[maxn],ch[maxn][27]; int head[maxm]; ll ans,id[maxm]; struct sj{int next,to;}a[maxm]; struct pp{ int x,siz; bool operator< (const pp &c) const {return c.sizvoid add(int x,int y)
    {
    a[++size].to=y;
    a[size].next=head[x];
    head[x]=size;
    }

    void insert(int x,char *s)
    {
    int len=strlen(s),u=0,lst=0;
    for(int i=len-1;i>=0;i--)
    {
    int p=s[i]-'a';
    if(!ch[u][p])ch[u][p]=++tot;
    u=ch[u][p];
    }
    pd[u]=x;
    return;
    }

    void pre(int x,string s)
    {
    int u=0,lst=0,len=s.length();
    for(int i=len-1;i>=0;i--)
    {int p=s[i]-'a';
    u=ch[u][p];if(pd[u]&&i!=0)lst=pd[u];}
    add(lst,x);
    }

    void dfs(int x)
    {
    t[x].x=x;t[x].siz=1;
    for(int i=head[x];i;i=a[i].next)
    {
    int tt=a[i].to;
    dfs(tt);
    t[x].siz+=t[tt].siz;
    }
    }

    void getans(int x)
    {
    priority_queueq;
    for(int i=head[x];i;i=a[i].next)
    {int tt=a[i].to;q.push(t[tt]);}
    while(q.empty()!=1)
    {
    pp now=q.top();
    id[now.x]=++tot;
    getans(now.x);
    ans+=id[now.x]-id[x];
    q.pop();
    }

    }

    char s[maxn];
    string c[maxm];
    int main()
    {
    cin>>n;
    for(int i=1;i<=n;i++)
    {
    scanf("%s",s);
    insert(i,s);
    int len=strlen(s);
    for(int j=0;j<len;j++)
    {c[i]+=s[j];}
    }
    for(int i=1;i<=n;i++)
    pre(i,c[i]);
    tot=0;
    dfs(0);
    getans(0);
    cout<<ans<<endl;
    }

  • 相关阅读:
    smdkv210
    wireshark常用过滤规则
    go json序列化不填充默认值
    refactoring.guru-重构-如何重构
    refactoring.guru--重构--何时重构
    refactoring.guru--重构--技术债务
    refactoring.guru学习记录--重构--整洁的代码
    window7下python3 编译pjsua
    go 单元测试框架介绍
    算法图解学习笔记之算法
  • 原文地址:https://www.cnblogs.com/Kv-Stalin/p/9484295.html
Copyright © 2020-2023  润新知