zoukankan      html  css  js  c++  java
  • [BZOJ4567][SCOI2016]背单词(Trie+贪心)

    1.题意表述十分难以理解,简单说就是:有n个单词,确定一个背的顺序,使总代价最小。

    2.因为第(1)种情况的代价是n*n,这个代价比任何一种不出现第(1)种情况的方案都要大,所以最后肯定不会出现“背某个单词的时候它的后缀还没背”的情况。

    3.考虑将每个串和单词表中它的最长后缀连边,则形成了一棵树。我们需要给树上每个点分配一个1~n的整数v[]且两两不同(就是背的顺序,要保证儿子分配到的数一定大于父亲)。那么总代价就是所有点的v[i]-v[fa[i]]。可以发现,要让总代价最小,最终的涂色序列(就是背的顺序)是这棵树的一个DFS序。

    4.关于建树,将所有串翻转变成前缀问题,再对所有串建Trie即可。

    5.考虑哪个DFS序能让总代价最小,考虑一个点的所有儿子,当走入一个儿子时,其它儿子和父亲的差就会+1,其余点不变。那么要让答案最小就必须要尽快从那个儿子中出来,于是肯定是选择size最小的那个儿子。

    这个结论全网都说十分显然但我既想不到也不会证,感觉自己贪心水平十分低下。

     1 #include<cstdio>
     2 #include<vector>
     3 #include<cstring>
     4 #include<algorithm>
     5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     6 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
     7 typedef long long ll;
     8 using namespace std;
     9 
    10 const int N=500010;
    11 int n,tim,nd=1,cnt,tot,top,h[N],to[N],nxt[N<<1];
    12 int id[N],fa[N],ch[N][27],stk[N],a[N],sz[N],v[N];
    13 ll ans;
    14 char s[N];
    15 vector<int>ve[N];
    16 
    17 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
    18 bool cmp(int a,int b){ return sz[a]<sz[b]; }
    19 
    20 void ins(int k,char s[]){
    21     int x=1,len=strlen(s+1);
    22     for (int i=len; i; i--){
    23         if (!ch[x][s[i]-'a']) ch[x][s[i]-'a']=++nd;
    24         x=ch[x][s[i]-'a'];
    25     }
    26     v[x]=k;
    27 }
    28 
    29 void dfs1(int x){
    30     if (v[x]) fa[v[x]]=stk[top],add(stk[top],v[x]),stk[++top]=v[x];
    31     rep(i,0,25) if (ch[x][i]) dfs1(ch[x][i]);
    32     if (v[x]) top--;
    33 }
    34 
    35 void dfs2(int x){ sz[x]=1; For(i,x) if ((k=to[i])!=fa[x]) dfs2(k),sz[x]+=sz[k]; }
    36 
    37 void dfs3(int x){
    38     tot=0; id[x]=++tim;
    39     For(i,x) if ((k=to[i])!=fa[x]) ve[x].push_back(k);
    40     sort(ve[x].begin(),ve[x].end(),cmp); int ed=ve[x].size()-1;
    41     rep(i,0,ed) dfs3(ve[x][i]);
    42 }
    43 
    44 int main(){
    45     freopen("bzoj4567.in","r",stdin);
    46     freopen("bzoj4567.out","w",stdout);
    47     scanf("%d",&n);
    48     rep(i,1,n) scanf("%s",s+1),ins(i,s);
    49     dfs1(1); dfs2(0); dfs3(0);
    50     rep(i,1,n) ans+=id[i]-id[fa[i]];
    51     printf("%lld
    ",ans);
    52     return 0;
    53 }
  • 相关阅读:
    登录不了路由器恢复办法
    刷完OpenWrt在浏览器无法访问的解决办法
    [海蜘蛛] 海蜘蛛 V8 全线无限试用版 免费发布破解教程
    ThinkPHP3.0启动过程
    ivr
    centos6.5下修改文件夹权限和用户名用户组
    从一条巨慢SQL看基于Oracle的SQL优化(重磅彩蛋+PPT)
    基于Docker搭建MySQL主从复制
    Elasticsearch全文检索实战小结
    springboot-Learning
  • 原文地址:https://www.cnblogs.com/HocRiser/p/10084993.html
Copyright © 2011-2022 走看看