zoukankan      html  css  js  c++  java
  • 洛谷P3294 [SCOI2016]背单词——题解

    题目传送

    阅读理解题题意解释可以看这位大佬的博客

      发现求后缀与倒序求前缀是等价的,而找前缀自然就想到了trie树。将所有字符串翻转后再建入trie树中,再对每一个字符串翻转后从trie树中找前缀,就能找到一个字符串的所有后缀了。

      由第三种情况知我们要想最小化总代价,则最小化一个字符串与最靠近它的后缀间的距离应该也是一个要考虑的因素。其实一个串最近的后缀其实一定是它的所有后缀中长度最大的,因为它的后缀中长度短的也一定是长度大的后缀,并且还要避免第一种情况的出现(即序列在一个字符串后面的串中不能有这个字符串的后缀)。故我们只要知道每个字符串最长的后缀就好。若一个串是另一个串的后缀,则可以从这个串向另一个串连一条有向边,则最终会形成一个森林。

      这时避免了第一种情况后,还有两种情况。我们做了这么多题,知道一种情况总应该比多种情况好做。故考虑将两种情况转化为一种。发现如果我们在所有题目给出的字符串的基础上再加入一个处于序列第0个位置的空串,那么第二种情况也就转化为了第三种情况,并且对答案没有影响。这种转化对于当前的森林,只要再建个标号为0的空串节点连向所有树的根就好了。

      此时问题就转化为:给树的节点编号,要求父亲节点的序号比儿子节点小,且根节点的序号为0,并使得儿子节点的编号减父亲节点编号的差的和最小,求最小的和。考虑怎么选点编号。作者一开始也懵的一逼于是看了看这位大佬博客关于选法的证明部分(“考虑建出……Q.E.D”)终于明白如果选到了一个点,就要一口气把它的子树都选了,并且优先选子树大小最小的儿子。dfs就搞定了呀。至于对某个点的儿子从子树大小小的往大的选,可以用堆维护。

    具体实现请看代码:

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<queue>
      5 
      6 #define max(a,b) ((a)>(b)?(a):(b))
      7 
      8 using namespace std;
      9 
     10 const int N=100005,LEN=510005;
     11 
     12 int tree[510005][26],cnt,n,l,dfs;
     13 int ed[LEN],in[N],lst[N],to[N],nxt[N],ecnt,dfn[N];
     14 
     15 long long siz[N],ans;
     16 
     17 string word[N];
     18 
     19 char ch;
     20 
     21 inline int read()
     22 {
     23     int x=0;
     24     ch=getchar();
     25     while(!isdigit(ch)) ch=getchar();
     26     while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
     27     return x;
     28 }
     29 
     30 inline void getstring(string &a)
     31 {
     32     a="";
     33     ch=getchar();
     34     while(ch<'a'||ch>'z')
     35         ch=getchar();
     36     while(ch>='a'&&ch<='z')
     37         a+=ch,ch=getchar();
     38 }
     39 
     40 inline void insert(const string &a,int j)
     41 {
     42     l=a.length();
     43     int now=0,num;
     44     for(int i=l-1;i>=0;--i)//要将字符串倒序插入trie树中 
     45     {
     46         num=a[i]-'a';
     47         if(!tree[now][num])
     48             tree[now][num]=++cnt;
     49         now=tree[now][num];
     50     }
     51     ed[now]=j;
     52 }
     53 
     54 inline void addedge(int u,int v)
     55 {
     56     nxt[++ecnt]=lst[u];
     57     lst[u]=ecnt;
     58     to[ecnt]=v;
     59 }
     60 
     61 inline int fin(const string &a)
     62 {
     63     int now=0,num,ret=-1;
     64     l=a.length();
     65     for(int i=l-1;i>=0;--i)//查后缀就是倒序查前缀 
     66     {
     67         num=a[i]-'a';
     68         if(!tree[now][num])
     69             return ret;
     70         now=tree[now][num];
     71         if(ed[now]&&i)
     72             ret=ed[now];
     73     }
     74     return ret;
     75 }
     76 
     77 void dfssiz(int u)
     78 {
     79     siz[u]=1;
     80     for(int e=lst[u];e;e=nxt[e])
     81     {
     82         dfssiz(to[e]);
     83         siz[u]+=siz[to[e]];
     84     }
     85 }
     86 
     87 struct node{
     88     int lar,ord;
     89 }head;
     90 
     91 inline bool operator < (const node &a,const node &b)
     92 {
     93     return a.lar>b.lar;//大根堆变小根堆 
     94 }
     95 
     96 void dfsans(int u)
     97 {
     98     priority_queue<node>hep;
     99     for(int e=lst[u];e;e=nxt[e])
    100     {
    101         hep.push((node){siz[to[e]],to[e]});    
    102     }
    103     while(!hep.empty())
    104     {
    105         head=hep.top();
    106         hep.pop();
    107         dfn[head.ord]=++dfs;//编号过程 
    108         ans+=dfn[head.ord]-dfn[u];
    109         dfsans(head.ord);
    110     }
    111 }
    112 
    113 int main()
    114 {
    115     n=read();
    116     for(int i=1;i<=n;++i)
    117     {
    118         getstring(word[i]);//字符串的读入优化 
    119         insert(word[i],i);
    120     }
    121     int u;
    122     for(int i=1;i<=n;++i)
    123     {
    124         u=fin(word[i]);
    125         if(u!=-1)
    126         {
    127             addedge(u,i);
    128             in[i]++;
    129         }
    130     }
    131     for(int i=1;i<=n;++i)
    132         if(!in[i])//没有入度的点就是森林中树的根 
    133             addedge(0,i);
    134     dfssiz(0);//求一下每个点的子树大小。 
    135     dfsans(0);//统计答案。 
    136     printf("%lld",ans);
    137     return 0;
    138 }
    AC代码
  • 相关阅读:
    【Nginx】ngx_event_core_module模块
    ELMAH--Using HTTP Modules and Handlers to Create Pluggable ASP.NET Components 77 out of 90 rated th
    nyist oj 214 单调递增子序列(二) (动态规划经典)
    java 入门书籍(java7)
    ARCGIS将WGS84坐标投影到高斯平面
    【linux】linux下对java程序生成dump文件,并使用IBM Heap Analyzer进行分析,查找定位内存泄漏的问题代码
    【springboot】【socket】spring boot整合socket,实现服务器端两种消息推送
    【linux】linux修改open file 大小
    【docker】docker限制日志文件大小的方法+查看日志文件的方法
    【docker】docker部署spring boot服务,但是docker logs查看容器输出控制台日志,没有日志打印,日志未打印,docker logs不打印容器日志
  • 原文地址:https://www.cnblogs.com/InductiveSorting-QYF/p/11808756.html
Copyright © 2011-2022 走看看