首页 > 其他分享 >P3294 [SCOI2016] 背单词 题解

P3294 [SCOI2016] 背单词 题解

时间:2024-07-25 12:56:17浏览次数:8  
标签:siz 结点 int 题解 dfs 后缀 P3294 字符串 背单词

题意

给你 \(n\) 个字符串,让你对其进行排列,使得按以下规则花费最少:

设当前字符串为 \(s\),\(x\) 为 \(s\) 在答案排列中的位置。

  1. 如果 \(s\) 存在后缀且 \(s\) 的后缀在 \(s\) 之后,花费加 \(n^2\)。

  2. 如果 \(s\) 不存在后缀则花费加 \(x\)。

  3. 设 \(y\) 为 \(s\) 之前离其最近的是 \(s\) 的后缀的字符串的位置,\(s\) 存在后缀且 \(s\) 的后缀在 \(s\) 之前,则花费加 \(x-y\)。

解法

对于 1 条件,显然尽量不要满足,因为 1 条件的花费永远比 2、3 条件多。

对于 2 条件,可以视为 3 条件中 \(y=0\),因此和 \(3\) 条件合并。

所以可以有一个贪心策略,对于一个字符串 \(s\),将它的若干后缀字符串放在 \(s\) 之前。那么此时仅存在 3 条件。

题目需要维护后缀,那么可以考虑将字符串反转,变成前缀,于是也可以想到对于反转的字符串建立 Trie 树。

既然要满足贪心策略,那么在 Trie 中,每一个字符串结尾的结点要在它的祖先结点之后。Trie 树就可以维护排列中的先后关系。

3 条件的花费为 \(x-y\),即排列中的距离,那么在 Trie 中,每一个字符串的结尾结点才需要考虑,因此考虑重构 Trie,将不是字符串结尾的结点删去。

考虑另一个贪心策略,由于 3 条件花费为 \(x-y\),那么 \(x\) 和 \(y\) 的距离越近越好,所以最优策略中的 \(x\) 和 \(y\) 在重构树中一定是父子关系。

由于我们需要 \(x-y\) 最小,所以我们尽可能地让 \(x\) 和 \(y\) 放一起,也就是遍历完父亲就遍历儿子。可以发现,这样的遍历顺序就是 dfs 序。

此时我们得到最优的遍历顺序为 dfs 序,对于一个结点 \(x\),可以发现在 dfs 序下,它和父节点的距离为之前遍历过的兄弟结点的子树大小和,也就是花费,因此可以对每一个结点按子树大小排序,即可得到最优解。

AC Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5;
int n, trie[N][27], cnt, last[N], siz[N], dfn, ans;
bool End[N];
vector<int> e[N];
void insert(string s)
{
    int x = 0;
    for(int _ = 0; _ < s.size(); _++)
    {
        char i = s[_];
        if(!trie[x][i - 'a'])
            trie[x][i - 'a'] = ++cnt;
        x = trie[x][i - 'a'];
    }
    End[x] = true;
    return;
}
void rebuild(int x)
{
    if(End[x] && x)
    {
        e[last[x]].push_back(x);
        last[x] = x;
    }
    for(auto y : trie[x])
    {
        if(!y)
            continue;
        last[y] = last[x];
        rebuild(y);
    }
    return;
}
void dfs(int x)
{
    siz[x] = 1;
    for(auto y : e[x])
    {
        dfs(y);
        siz[x] += siz[y];
    }
    sort(e[x].begin(), e[x].end(), [](int x, int y)
    {
        return siz[x] < siz[y];
    });
    return;
}
void dfs1(int x)
{
    int k = dfn;
    dfn++;
    for(auto y : e[x])
    {
        ans += dfn - k;
        dfs1(y);
    }
    return;
}
signed main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        string s;
        cin >> s;
        reverse(s.begin(), s.end());
        insert(s);
    }
    End[0] = true;
    rebuild(0), dfs(0), dfs1(0);
    cout << ans;
    return 0;
}

标签:siz,结点,int,题解,dfs,后缀,P3294,字符串,背单词
From: https://www.cnblogs.com/Luckies/p/18322774/P3294

相关文章

  • P10717 题解
    好神仙的题目。赛时胡了一个状态和转移都和官解不同的做法,得到了\(O(n10^m)\)的优秀复杂度。卡了一场常卡进了\(75\)分。这个做法和官解关系不大,并且很难进行最后的优化部分,所以在此不再赘述。首先考虑\(k=1\)的情况。考虑记录一些状态能够描述子树内的选择方案,\(0\)表示......
  • 题解:Codeforces Round 961 (Div. 2) A
    A.Diagonals*timelimitpertest:1secondmemorylimitpertest:256megabytesinput:standardinputoutput:standardoutputVitaly503isgivenacheckeredboardwithasideof\(n\)and\(k\)chips.Herealizedthatallthese\(k\)chipsneedto......
  • [题解]CF958C3 Encryption (hard)
    思路先考虑\(\Theta(n^2k)\)的暴力DP。定义\(dp_{i,j}\)表示在前\(i\)个数中选取\(j\)个的最小和,转移显然:\[dp_{i,j}=\min_{1\leqk<i}\{dp_{k,j-1}+s_{k+1,i}\bmodp\}\]注意到一个性质:\(dp_{i,j}\equivs_i\pmodp\)。因为前者是前\(i\)项分为若干......
  • Java二叉树经典进阶OJ题解
     目录一、判断一颗二叉树是否为对称二叉树1.题目描述:2.代码示例:3.通过演示与分析:二、根据先序遍历结果构造二叉树1.题目描述:2.代码示例:3.通过演示与分析:三、层序遍历的非递归实现1.题目描述:2.代码示例:3.通过演示与分析:四、判断是否为完全二叉树1.题目描述:2.......
  • dify-on-wechat 数据乱串问题解决记录
    在检查dify-on-wechat应用中的数据乱串问题时,我们发现了一个关键因素:当前端和后端使用的大语言模型不一致时,会导致数据串扰问题。在深入调查和测试后,我们采取了将前后端的大语言模型改为一致的策略。在实施这一改变后,数据乱串的问题得到了有效解决。以下是详细的记录:问题现象:在dif......
  • 密码学-RSA基础题解题脚本-Python
    importgmpy2#计算大整数模块importlibnumimportrsafromCrypto.PublicKeyimportRSA#安装时安装pycryptodome模块#已知:p,q,e,cdefknown_p_q_e_c():p=int(input('请输入一个素数p:'))q=int(input('请输入另一个素数q:'))e=int(input('请输入公钥e:'))......
  • [题解]P9755 [CSP-S 2023] 种树
    P9755[CSP-S2023]种树迟来的补题本题是让最小化所有树长到指定高度日期的最大值,于是想到二分答案。那么,对于一个给定的期限\(x\),如何判断是否能在这个日期内完成任务呢?首先我们发现前\(n\)天每天都要种树,那么假设我们已经知道了每个地块最晚哪个日期种树,能保证在期限\(x\)......
  • codeforces div_2 961 题解报告
    codeforcesdiv_2961题解报告比赛链接:https://codeforces.com/contest/1995A.Diagonals题目翻译给定一个边长为\(n\)的正方形,给定\(k\),要往正方形选\(k\)个点,\(x+y\)相同的点构成对角线,问至少要几条对角线才能装下\(k\)个点。时限1s,空间限制256MB\(1\len\le100,0\l......
  • 题解:牛客多校第三场 A
    ABridgingtheGap2时间限制:C/C++1秒,其他语言2秒空间限制:C/C++1048576K,其他语言2097152KSpecialJudge,64bitIOFormat:%lld题目描述Agroupof\(n\)walkersarrivesatariverbankatnight.Theywanttocrosstheriverusingaboat,whichisinitiallyont......
  • [题解]P3187 [HNOI2007] 最小矩形覆盖
    P3187[HNOI2007]最小矩形覆盖调了半天居然是因为没判断浮点精度误差才\(\colorbox{IndianRed}{\texttt{\color{White}{WA}}}\)了\(3\)个点,其他都没有问题!警钟长鸣……首先有一个结论:凸多边形的最小外接矩形一定和它的一条边重合。这个结论,网上几乎找不到完整的证明,目前发现......