codeforces #556(div1)

B. Three Religions

dp
题意:给一个主串和3个子串,每次可以在一个串后面增加或者删除一个字符。问3个串与主串的相同子序列是否可以不交叉。

首先不考虑加入或者删除,那么用dp[i][j][k]表示3个子串分别匹配到第i , j , k位置在主串最小位置在哪。然后用个nxt[i][26]表示主串每个位置且后面填’a’-‘z’会跳到主串的哪个位置,预处理倒着扫一遍就可以了。这样可以O(2503)跑出其中一个答案。
考虑修改和查询,那么其实影响的只有一维状态,例如加入i维时, dp[1 ~ i-1][1 ~ j][1 ~ k]状态是不变的,所以只需要更新dp[i][1 ~ j][1 ~ k]的状态就行了,删除同理。
所以时间复杂度为O(2502q)。

C. Tree Generator™

线段树
题意:每一颗树都可以变成一个匹配的括号序列,左括号表示添加深度多一层的节点,右括号表示回到上一层的节点。(貌似就是dfs序)然后有1e5个询问,问你如果交换两个不相同的括号(保证合法匹配)的情况下,这颗树的直径是多少?
首先考虑,树的直径就是树上最大的两点距离。那么令d[u]表示u到根节点的距离,那么直径就等于max(d[u] -2d[lca(u,v)] +d[v])。然后通过观察,我们可以发现这个括号序列的前缀和就是这个点所在的深度,且对于序列中两个位置,他们的lca一定出现在他们中间(可能一个点在序列中会多次表示,但不影响)。那么初始情况的我们可以先将所有位置的深度求出来,然后从左往右扫,维护一下d[u],d[u]-2d[lca],d[u]-2d[lca]+d[v] 的值,我们就可以O(n)扫一遍获得答案。
带修改我们就可以用线段树来维护。
首先对于一段区间(不要求完全匹配),我们可以单独维护其
d[u],-2d[lca],d[u]-2d[lca],2d[lca]+d[v],d[u]-2d[lca]+d[v] 那么合并两端区间的时候我们可以发现,右区间相当于接上左区间形成一棵树,所以右区间进行合并的时候需要加上左区间的结束深度的贡献。所以多维护一个深度的量。这样就可以维护出直径了。
对于修改,单点修改到叶子。然后不断更新上去就行了。这道题的pushup要注意挺多细节。

D. Abandoning Roads

考察最小生成树的构建过程和dijkstra的算法过程
题意:给你<=70个点,<=200条边。每种边的权值要么是a,要么是b(a<b)。然后以1为源点,分别以(1~n)为汇点。问构建一颗最小生成树,使得源点到汇点的距离最小。问最小值是多少。对于每个点都输出结果。
首先我们将所有以a边连在一起的点变成一个连通块。那么可以证明,连通块不存在b边的路径。考虑克鲁斯卡尔的算法过程,因为遍历完a边,连通块的点就肯定已经连通,那么块内肯定不存在b边。且对于一个b边,不能经过一个连通块两次。我们可以将连通块缩成一个点,那么我们求最小生成树就是在这些点和b边中得出,因为是树所以不能有环,显而易见。
然后我们就可以用dijkstra跑出答案,设d[S][i]表示当前已经遍历了S状态的连通块,且当前点在i点上的最短路。然后min(d[S][i])的值就是i点的答案。不过连通块最多有70个时间复杂度为O(2n mlog(2n m)会炸
一个优化就是如果连通块的节点个数小于等于3,那么我们就不用将他记录到状态上。
因为首先如果对于一个连通块b边需要经过两次,那么意味着需要两条b边。3个点的连通块最多有2条a边,2a<2b所以在dijkstra的扩展过程中扩展不到2b所以可以直接忽略。然后时间复杂度就变成O(2(n/4) m log(2 (n/4)m))就可以通过了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值