P3294

trie 树好题,题面差评。

题意和思路

P3294 [SCOI2016]背单词
给定 n 个字符串,要求对其进行排序,使得总贡献最小。
假设插入某个排序后编号为 x 的字符串 s,产生的贡献规则为:
1.如果在 s 后的字符串中有 s 的后缀,贡献为 n2
2.如果在 s 前的字符串中有 s 的后缀,且 s 后无 s 的后缀,则贡献为 xyy 是上一个是 s 的后缀的字符串排序后的编号)。
3.如果 s 无后缀(单个字符),则贡献为 x

对于规则 3,其实可以看出其实就是规则 2 中 y=0 的特殊情况。

因为无论如何,xy 必然远小于 n2,所以贪心的思路很简单,就是将每个字符串的后缀都放到其之前的前提下,使得最后一个此字符串的后缀与字符串之间的距离最小即可。

做法

将所有字符串翻转,建立 trie 树,并在每个字符串的末尾编号。

因为将所有字符串翻转就能把后缀转变为前缀,从而便于用 trie 树处理前缀问题。

对所有编号的节点建重构树。
遍历原树上每个节点,分情况进行不同的操作,采用并查集判是否属同一集合。
1.如果某个节点的某个子节点没有被编号,就将其与当前节点并查集合并。
2.否则把子节点作为一个新的节点,与当前节点的集合连一条边。

之前编号是为了之后跑出重构树,原先树中没有编号的点与后续操作无关,只需保留根节点和每个字符串的末尾的节点即可。

建重构树的原因是,要统计每个子树的关键节点个数。

统计重构树的每个节点的子树大小后,以此为关键字排序。

比较重构树的每个节点子树的关键节点个数的原因是对于某个节点,其贡献是其前面所有树的节点个数之和,所以要对每个节点的所有子树所含关键节点数为关键字排序。

最后按照题目给出的规则统计和即可。

记得最后统计答案的变量要开 long long。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;const int N=5e5+1e4;
int n,len[N],size[N],id[N],c,u,cnt,tot,t[N][30],flag[N],f[N],v,ans;string s[N];
vector <int> e[N];int cz(int x){return x==f[x]?x:f[x]=cz(f[x]);}
int R(){
    int x=0,f=0; char ch=getchar();while(!isdigit(ch)) f|=(ch==45),ch=getchar();
    while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();return f?-x:x;
}void W(int x){if(x>9)W(x/10);putchar(x%10+'0');}bool cmp(int x,int y){return size[x]<size[y];}
void ins(int x){u=0;for(int i=len[x]-1;i>=0;i--){c=s[x][i]-'a';if(!t[u][c])t[u][c]=++tot;u=t[u][c];}flag[u]=x;}
void pre(int x){for(int i=0;i<26;i++)if(v=t[x][i]){if(!flag[v])f[v]=cz(x);else e[flag[cz(x)]].push_back(flag[v]);pre(v);}}
void dfs(int x){size[x]=1;for(int i=0;i<e[x].size();i++){dfs(e[x][i]);size[x]+=size[e[x][i]];}sort(e[x].begin(),e[x].end(),cmp);}
void ask(int x){id[x]=cnt++;for(int i=0;i<e[x].size();i++){ans+=cnt-id[x];ask(e[x][i]);}}
signed main(){
    n=R();for(int i=1;i<=n;i++)cin>>s[i],len[i]=s[i].length();for(int i=1;i<=n;i++)ins(i);
    for(int i=1;i<=tot;i++)f[i]=i;pre(0);dfs(0);ask(0);cout<<ans<<endl;return 0;
}
posted @   AIskeleton  阅读(16)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示