把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

题解 P7537 [COCI2016-2017#4] Rima

洛谷

题意

显然。

分析

首先,我们要求的是最长公共后缀,这显然并不便于我们分析,因此,我们要做的第一件事就是将我们输入的串翻转。

第二,分析我们条件的性质。

LCP(A,B)max(|A|,|B|)1,这代表了什么,我们分析考虑。(下默认 |A||B|

  1. |A|<|B|1,显然不成立。
  2. |A|=|B|1,当 AB 的前缀时成立。
  3. |A|=|B|AB 的前 |A|1 都相同。

由于我们的长度不同以及题目的条件限制,我们显然不能直接线性 DP,因此,我们按照长度分类。

我们发现,由 A 寻找一个长度大于他的符合条件 B 是有 26 种可能的(或者更多,因为没有限制都是小写字母),但是我们由 B 推至 A 却是唯一的。

这让我们想到了树的性质(考试的时候没有想到 Trie 树,后来发现 Trie 一方面卡空间,另一方面限制了只能是小写字母),因此,我们将 AA 的前 |A|1 连一条边(后称 preA)。

怎么连边呢,我们先将所有的 sipresi 给予一个 hash 值(字符串哈希即可),然后离散化,再进行连边即可,可能时间上会多一个 log,但是无伤大雅,空间却只用开 2×n

int cnt=0;
for(int i=1; i<=n; ++i) {
scanf("%s",s);
int len=strlen(s);
ull tot=0;
for(int j=len-1; j; --j) tot=tot*P+s[j];
ull pre=tot;
tot=tot*P+s[0];
u[i]=tot,v[i]=pre;
num[++cnt]=u[i],num[++cnt]=v[i];
}
sort(num+1,num+cnt+1);
cnt=unique(num+1,num+cnt+1)-num-1;
for(int i=1; i<=n; ++i) u[i]=lower_bound(num+1,num+cnt+1,u[i])-num;
for(int i=1; i<=n; ++i) v[i]=lower_bound(num+1,num+cnt+1,v[i])-num;
for(int i=1; i<=n; ++i) val[u[i]]=1,lj[v[i]].push_back(u[i]);

接下来解决重头戏:树形 DP。

找一下最终答案的性质,易于发现的,我们最终答案在长度上要么是单调递降,要么单调递增(与前者相同),要么是先下降后上升。放在树上可以发现,这就是我们垂下的两条链,所以,在大的方向上与树形 DP 求数的直径相同。

接下来是转移答案的过程,我们在分析时一直都忽略了一个问题,在长度相同的时候的合法情况,在树上时,这些长度相同的都会挂在同一个节点之下,这些节点我们直接取来即可,在最后将其重复贡献减去即可。

inline void dfs(int now) {
if(vis[now]) return ;//标记是否走过
vis[now]=1;
int tot=0;
for(auto to:lj[now]) {
if(!val[to]) continue;//不走构造出的不存在的 pre
dfs(to);
sec[now]=max(sec[now],mx[to]);
if(sec[now]>mx[now]) swap(mx[now],sec[now]);
++tot;
}
ans=max(mx[now]+sec[now]+tot-min(tot,2)+val[now],ans);//减去重复贡献
mx[now]=mx[now]+tot-min(tot,1)+val[now];//继承最长的
}

在实现时,我们需要遍历每一个节点,包括我们构成的虚点,这样才能保证不漏记。

总结一下,这题考验了树形 DP 的转化能力。

posted @   djh0314  阅读(21)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示