当前位置: 首页 > news >正文

【HT-086-Div.2】错乱的集合

比赛现场

更阅读体验的阅读体验

是个好题。但是我赛时怎么什么都不会。


首先简化一下题面:\(s\)\(t\) 被认为是相同的,当且仅当 \(s=t\)\(|t|=|s|-1\)\(t\)\(s\) 的后缀(或者反过来 \(s\) 是后缀)。

(以下都假设 \(s\) 是较长的那个)

那这样的话,\(s\) 就是 \(t\) 前面多一个字母。\(s\) 如果在集合里的话,\(t\) 就不能在集合里。反之亦然。

我们又考虑到,\(s\) 只会有一个对应的后缀 \(t\),但是 \(t\) 前面可以加任意字母构成任意的 \(s'\),也就是 \(t\) 会和多个较长的 \(s'\) 构成不合法关系。换句话说,这是个一对多的关系,约等于一个森林。

那这不就是《没有上司的舞会》吗?对的对的,如果我们把树建出来的话就能直接套用那个题的做法了。

那咋建树呢?或者说对于一个字符串 \(s\) 的话,怎么让它所有的前缀和它们不合法的那个后缀建上边呢?或者我们怎么找不合法后缀呢?

关键词:前缀。考虑用 Trie 树。我们把所有的串串扔进一个 Trie 树里。

原题解这里讲的不太清楚。我的理解是,对于第 \(i\) 个串 \(s_i\),我们从小到大枚举 \(j\) 表示当前这个前缀截止到 \(j\) 这个位置。

(由于我的坐标从 1 开始,所以我的 \(j \in [1,len]\)。)

我们对于每个 \(j\) 要看看是否有一个不合法后缀 \(s_{2,3,\cdots,j}\) 存在于 Trie 树上,有的话说明存在这样的前缀不能和当前前缀一个集合,我们就将当前前缀在树上的编号与这个前缀在树上的编号建边。

显然 \(j=1\) 时是没有这样的后缀的。当 \(j \in [2,len]\) 时,每当 \(j \to j+1\),那么当前要找的不合法后缀也会加一个对应字符。

比如我们考虑 abb 这个前缀的时候,它要找的不合法后缀是 bb。当我们考虑完这个位置,考虑 abba 这个前缀的时候,要找的不合法后缀也会多一个字母 a 变成 bba

这对应到 Trie 树上是什么?假设我们已经找完了 \(j\) 位置的不合法后缀,我们找 \(j+1\) 的时候,让 \(now \to tr_{now,s[i][j+1]}\),在树上往下跳即可。

如果找到了就像前面说的一样建边,如果找不到了就说明没有这样的后缀了,后面的前缀也不会再有了,直接跳出循环。

这样我们把树建好以后,跑一遍树上 dp 即可。

代码:

T2代码
#include<bits/stdc++.h>
#define int long long
using namespace std;inline int read(){int x=0,f=1;char c=getchar();while(c<48){if(c=='-') f=-1;c=getchar();}while(c>47) x=(x<<1)+(x<<3)+(c^48),c=getchar();return x*f;
}const int N=1e6+6;
int T,n,tr[N][30],awa,dp[N][2],h[N],fa[N],tot;
//tr:Trie 树
//awa:当前Trie树节点开到哪了 
//dp:树上dp数组
//fa[i]:Trie树上编号为 i 的点要向哪个点连边 
vector<int> pos[N];
//pos[i][j]:第 i 个串长度为 j 的前缀对应 Trie 树上的哪个点 
string s[N];
struct sw{int u,v,nxt;
}e[N];inline void INIT(){for(int i=0;i<=awa;i++){dp[i][0]=dp[i][1]=0;fa[i]=0;h[i]=0;for(int j=1;j<=26;j++){tr[i][j]=0;}}awa=0;for(int i=1;i<=tot;i++){e[i]={0,0,0};}tot=0;
}inline void INITT(){for(int i=1;i<=n;i++){pos[i].clear();s[i]=' ';}
}inline void add(int u,int v){e[++tot]={u,v,h[u]};h[u]=tot;
}inline void dfs(int u){//树上dp,不会的可看P1352,不过这里每个点的点权是1 dp[u][1]=1;for(int i=h[u];i;i=e[i].nxt){int v=e[i].v;dfs(v);//考虑选u点的情况,此时子节点不能选 dp[u][1]+=dp[v][0];//考虑不选u点的情况,此时子节点任意 dp[u][0]+=max(dp[v][0],dp[v][1]);}
}inline void ins(int id){//Trie树里的插入操作 int len=s[id].size()-1,now=0;pos[id].push_back(0);for(int i=1;i<=len;i++){int fu=s[id][i]-'a'+1;if(!tr[now][fu]){tr[now][fu]=++awa;}   now=tr[now][fu];pos[id].push_back(now);}
}signed main(){freopen("b.in","r",stdin);freopen("b.out","w",stdout);T=read();while(T--){//多测记得初始化 INIT();n=read();INITT();for(int i=1;i<=n;i++){cin>>s[i];s[i]=' '+s[i];ins(i);}//处理每个点往哪里连边 for(int i=1;i<=n;i++){int now=0,len=s[i].size()-1;for(int j=2;j<=len;j++){int fu=s[i][j]-'a'+1;if(!tr[now][fu]){//没有找到不合法后缀,说明后面的点也找不到了 now=-1;break;} now=tr[now][fu];//否则当前前缀记录往now上连边 fa[pos[i][j]]=now;}}//我们发现,对于没有限制的点,默认会往 0 号点连边,这样不仅是正确的(它们在dp里一定会被选),还不用再单独处理这种情况了 for(int i=1;i<=awa;i++){add(fa[i],i);}dfs(0);//由于 0 号点是虚拟的,所以选了没有意义,且有可能搞掉正确答案的选法 int ans=dp[0][0];printf("%lld\n",ans);}return 0;
}
http://www.zskr.cn/news/49809.html

相关文章:

  • WEditor的使用方法
  • 感情粉末沿着试管边缘 在祝福中逐渐分解 加热认知离子重新排列 于底部悲伤沉淀
  • flask: 抛出异常
  • 雪地奔驰全等级提升所需经验一览
  • 2025皮肤亚健康管理品牌最新专业推荐:科技赋能健康美新生态
  • 深入解析:Vue3 路由配置和使用与讲解(超级详细)
  • HubSpot如何规模化推进AI编码助手应用
  • 完整教程:OpenHarmony内核基础:LiteOS-M内核与POSIX/CMSIS接口
  • 常量指针 和 指针常量 - const pointer and pointer to const
  • 11.14模拟赛
  • 实用指南:云计算生态及学习方向和就业领域方向
  • 2025年11月徐州AI GEO平台综合评测与权威推荐
  • 2025年国内徐州宣传片公司品牌权威推荐榜单
  • 好题集 (2) - LG P4550 收集邮票
  • 腾讯元宝如何导出内容为文档
  • 洛谷 P4242. 树上的毒瘤
  • Number Theory
  • 实用指南:【STM32】RTC实时时钟
  • 探索乐泰胶水:性能与适用场景全解析
  • 【System Beats!】第七章 链接
  • 实用指南:接口测试 | 使用Postman实际场景化测试
  • 应用程序建立的数据库连接,也就是非交互式连接 是什么时候开始的?什么时候结束?连接结束后 会影响应用程序操作db失败吗? 还有就是如果连接关闭了 会立马重新建立新的连接吗?
  • #题解#洛谷P1884#二维离散化#
  • HarmonyOS应用配置文件与资源组织深度解析 - 教程
  • 2025厨房/无烟管/商用/复合式/内循环/小型/油烟净化/一体机推荐榜:上海多环五星领跑 全场景适配解锁餐饮 / 家用净化新体验
  • 2分钟选刊!值得农林环境人收藏的6个期刊!境科研人必备!
  • 2025武汉车出租厂家推荐榜:防撞车出租/高空车出租/登高车出租/服务体验与高性价比深度解析
  • 2025试验机厂家推荐榜:万能试验机/高低温试验机/钢丝绳试验机专精之选
  • 革命你的 Git 提交消息 - GIM 1.8.0 发布了!
  • 深入解析:【具身智能】具身机器人VLA算法入门及实战(三):VLA经典模型架构