[SCOI2016]背单词

emm。。。可以允许我吐槽一下出题人的语文吗。。。这个题题面杀?!

emm。。。毕竟是。。。细川银?!

这个题运用(百度)一些操作。。。我终于看懂了题面。。。QVQ

题目大意:

给你n个字符串,不同的排列有不同的代价,代价按照如下方式计算(字符串s的位置为x):

1.排在s后面的字符串有s的后缀,则代价为n^2;

2.排在s前面的字符串有s的后缀,且没有排在s后面的s的后缀

 则代价为x-y(y为最后一个与s不相等的后缀的位置,注意:是位置);

3.s没有后缀,则代价为x。

求最小代价和。

这样的话大佬们是不是就一眼秒了。。。

这样我们实行一个贪心策略貌似就可以了。。。

当然我们可以发现,如果你用到了第一个条件,那么肯定不是最优的。。。

由于我们是找后缀,所以呢,我们把正着给我们的字符串反过来

建立一棵 trie

这样就变成了一颗树,问题转化为给树上每个点标号,每个点的标号大于其父亲的编号,

每个点代价为他的标号减去其父亲标号,最小化代价

证明?

emm。。。博主窝是手玩dfs序玩出来了。。。

但是事实上严格证明貌似比较难搞啊。。。这里顺带把几个主要步骤也说了。。。

1.由trie树的性质我们可以知道,它会很机智的合并字符串重复的部分。。。

2.那么当我们对于每一段进行dfs,这个地方很清楚,如果你要取一个单词,

 那么这个单词的后缀肯定需要先取到,所以优先级就很明白了。。。

 这时我们在dfs中记录下来这条路径中包含的单词从短到长存入,第1梯队第2梯队。。。以此类推

3.对于已经得到的这些梯队我们还要通过一遍dfs,求得排序的优先级。。。

 只需要求一段树上前缀和就可以了。。。

4.到了最关键的统计答案的地方,我们通过比较可以发现,对已知的有先后顺序的梯队,

 对于每个梯队来讲,我们假设只有两个梯队 x1 x2 在第一梯队里,而 size[x1] > size[x2],

 如果把影响多的放在前面,因为以 x1 为后缀的单词比 x2 多

 那么这样造成的差值肯定比 x2 放在前面大,意会一下。。。

 写完dfs1,dfs2然后自己出数据手玩一下就好了。。。

 统计答案只需要重新定义它现在的位置,和dfs到了第几个,然后计算就好了

emm。。。本蒟蒻的呆码会被最水的 n=2 的数据卡掉。。。QVQ。。。惨啊~~~

但是没人会出这种送分数据吧。。。→_→

呆码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;

int n,rt=1,t[510010][27];
int cnt=1,num=1,sum;
long long ans;
int top[510010],siz[510010];
char ch[510010];
bool mark[510010];

vector <int> v[100010];

inline void insert(char *ch)
{
    int l=strlen(ch+1); int now=rt,u;
    for(int i=l;i;i--){
        u=ch[i]-‘a‘;
        if(!t[now][u])t[now][u]=++cnt;
        now=t[now][u];
    }
    mark[now]=1;
}

inline void dfs1(int x)
{
    if(mark[x]) { v[top[x]].push_back(++num); top[x]=num; }
    for(int i=0;i<=25;i++)
        if(t[x][i])
        {
            top[t[x][i]]=top[x];
            dfs1(t[x][i]);
        }
}

inline void dfs2(int x)
{
    siz[x]=1; int u;
    for(int i=0;i<v[x].size();i++)
    {
        u=v[x][i];
        dfs2(u);
        siz[x]+=siz[u];
    }
}

inline bool cmp(int x,int y) { return siz[x]<siz[y]; }

inline void dfs3(int x)
{
    top[x]=++sum;
    sort(v[x].begin(),v[x].end(),cmp);
    for(int i=0;i<v[x].size();i++)
    {
        ans+=sum+1-top[x];
        dfs3(v[x][i]);
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ch+1);
        insert(ch);
    }

    top[rt]=1;
    dfs1(rt);
    dfs2(1);
    dfs3(1);
    printf("%lld\n",ans);
    return 0;
}

过不了样例的AC程序

原文地址:https://www.cnblogs.com/zzzyc/p/8846657.html

时间: 2024-10-17 15:51:42

[SCOI2016]背单词的相关文章

【BZOJ4567】[Scoi2016]背单词 Trie树+贪心

[BZOJ4567][Scoi2016]背单词 Description Lweb 面对如山的英语单词,陷入了深深的沉思,“我怎么样才能快点学完,然后去玩三国杀呢?”.这时候睿智 的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的: ————— 序号  单词 ————— 1 2 …… n-2 n-1 n ————— 然后凤老师告诉 Lweb ,我知道你要学习的单词总共有 n 个,现在我们从上往下完成计划表,对于一个序号为 x 的单词(序号 1...x-1 都已经被

P3294 [SCOI2016]背单词

P3294 [SCOI2016]背单词 Trie+贪心 倒插进树+取出重建+子树处理+贪心遍历 倒插进树:把后缀转化为前缀,所以把字符串倒着插进Trie中 取出重建:重新建立一棵以单词为节点的树,如果存在包含(前缀)关系就连边 子树处理:处理出每棵树的大小用于贪心 贪心遍历:遍历整棵新树,累加答案 关于贪心:每次找到最小的子树统计答案 end. #include<iostream> #include<cstdio> #include<cstring> #include&

[bzoj4567][Scoi2016][背单词] (贪心+trie树)

Description Lweb 面对如山的英语单词,陷入了深深的沉思,“我怎么样才能快点学完,然后去玩三国杀呢?”.这时候睿智 的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的: ————— 序号  单词 ————— 1 2 …… n-2 n-1 n ————— 然后凤老师告诉 Lweb ,我知道你要学习的单词总共有 n 个,现在我们从上往下完成计划表,对于一个序号为 x 的单词(序号 1...x-1 都已经被填入): 1) 如果存在一个单词是它的后缀,并且

BZOJ4567: [Scoi2016]背单词

Description Lweb 面对如山的英语单词,陷入了深深的沉思,“我怎么样才能快点学完,然后去玩三国杀呢?”.这时候睿智 的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的: ————— 序号  单词 ————— 1 2 …… n-2 n-1 n ————— 然后凤老师告诉 Lweb ,我知道你要学习的单词总共有 n 个,现在我们从上往下完成计划表,对于一个序号为 x 的单词(序号 1...x-1 都已经被填入): 1) 如果存在一个单词是它的后缀,并且

bzoj 4567: [Scoi2016]背单词

Description Lweb 面对如山的英语单词,陷入了深深的沉思,"我怎么样才能快点学完,然后去玩三国杀呢?".这时候睿智 的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的: ----- 序号 单词 ----- 1 2 -- n-2 n-1 n ----- 然后凤老师告诉 Lweb ,我知道你要学习的单词总共有 n 个,现在我们从上往下完成计划表,对于一个序号为 x 的单词(序号 1...x-1 都已经被填入): 1) 如果存在一个单词是它的后

[Trie][LuoguP3294][SCOI2016]背单词

题意很迷,然后你可以发现一种情况就是为了告诉你这是贪心. 显然我们可以将单词倒序插入Trie树,然后按照子树内单词个数从小到大遍历,模拟统计答案. 然后我们就得到了一个优秀的40分代码. #include <string.h> #include <stdio.h> #include <iostream> #include <vector> #include <algorithm> #define LL long long #define ULL

[luogu] P3294 [SCOI2016]背单词 (贪心)

题目描述 Lweb 面对如山的英语单词,陷入了深深的沉思,"我怎么样才能快点学完,然后去玩三国杀呢?".这时候睿智的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的: -----序号 单词----- 1 2......n-2n-1 n----- 然后凤老师告诉 Lweb ,我知道你要学习的单词总共有 n 个,现在我们从上往下完成计划表,对于一个序号为 x 的单词(序号 1...x-1 都已经被填入): 如果存在一个单词是它的后缀,并且当前没有被填入表内

【bzoj4567】SCOI2016背单词

题号莫名喜感. 倒序建Trie,dfs这棵Trie,贪心一下,每次按照size排序计算贡献就好. #include<bits/stdc++.h> #define N 100010 #define M 500010 using namespace std; typedef long long ll; ll ans=0; int ch[M][26],val[M],tpos[M],cnt,rt,tot,n,size[M]; char s[M]; vector<int> a[N]; inl

Luogu P3294 【[SCOI2016]背单词】

阅读理解题 ...... $Trie$ 后缀问题不好处理,我们把它转化为前缀问题,用字典树解决问题 贪心 容易想到,一个串的后缀要先于它插入 对于一个串和其若干后缀串,容易想到,我们要先插入后缀串 然后递归进入$size$最小的子串 bool cmp(const int &x,const int &y) { return size[x]<size[y]; } void makes(int x) { size[x]=1; for(int i=0;i<t[x].size();i++