第十五周讨论:
一、网络文化大赛
二、机房出勤
(空调已修好)
以后多讲题为主,由题目引入知识点,概念讲多了太烦躁了
三、大三讨论
1. POJ 3683 Priest John’s Busiest Day
看这个题目之前可以先看POJ2186复习一下强联通分量的分解
题意:给出N个开始时间和结束时间和持续时间三元组,持续时间可以在开始后或者结束前,问如何分配可以没有冲突。
—————–我是分割线———————————
先解释一下合取范式(离散数学已经学过):
如果合取范式中的每个字句的文字个数不超过两个就称为2-SAT问题
一般性称为n-SAT问题
举个栗子: 在a为false而b为true时整个范式的取值为真。
利用强连通分量的知识,就可以在布尔公式字句个数的线性时间内解决2-SAT问题。在离散数学中我们已经学过蕴含范式。对于可以转换为
下面就是建图过程了:
对于每一个布尔变量x,构造两个顶点x和;以为有向边建立有向图。
在有向图中,如果a能到达b的话,a为真则b也为真。
因此在同一个强连通分量中所含的所有文字代表的布尔值都相同。
特别注意的是,假设x和都在同一个强连通分量中,则显然,这个强连通分量始终不可能为真。
相反,如果不存在这样的布尔变量,对于每个布尔变量x,让
x所在的强连通分量的拓扑序在所在的强连通分量之后,(也就是比较二者的拓扑序)
就是使得该公式的值为真的一组合适的布尔变量的解。
——————-我是分割线————————-
对于每个三元组,只有在开始之后和结束之前两种选择,不妨设变量xi
xi为真<->在开始之后开始插入时间长度
有了这些理论支持,对于每个三元组,无非有四种组合情况:
开始-开始
开始和结束
结束-开始
结束-结束
(本题中样例中没有结束-开始)
如果开始-开始冲突,那么的值为真。
所以合取范式为:
当x1的值为真x2的值为假时,其值为真。
接下来就是进行强连通分量分解并判断是否有使得布尔公式的值为真的一组布尔变量赋值。
(这里利用带了前面的那个定理:如果x所在的强连通分量的拓扑序在 。
AC代码:
https://paste.ubuntu.com/p/B2Wdxqbcqr/
2.POJ2186
题意是:求其他所有牛都认为是Popular的牛的个数!具体规则在原题中
本题如果没有强联通分量的的知识的话,很容易会想对每一个其他顶点来一遍DFS,如果其他顶点对当前顶点都可达,说明这个顶点是被其他牛认为popular,然而时间复杂度为O(N*M)
有了强联通分量的知识就好办了,对图进行强联通分量分解,至多有一个强联通分量满足条件。在进行强联通分量分解后,可以得到强联通分量拓扑排序后的顺序,唯一能成为解的是拓扑排序最后面的的强联通分量。时间复杂度O(M+N)
AC代码:
https://paste.ubuntu.com/p/dGDFfy3GpZ/
3. 校赛E 比比谁更快
题意:给你一个字符串,各两个操作:
ch=0,[l,r]降序
ch=1,[l,r]升序
如果是newer的话,应该会想用暴力,直接对区间sort,但是很明显是超时的(校赛怎么会这么简单呢!)
很容易想到线段数,对于线段数,我们可以先理清一下知识点,线段树大概有三种操作:
- 单点更新
- 区间更新
- RMQ
显然这里要用区间更新的操作。
区间更新有三大操作:
build()建树
update()更新
query()求和
sum[4*N]存储线段数节点
但是一般的区间更新的线段数是区间加或者区间减等统一的操作,所以对于区间排序需要对区间查询模板更改一下。
sum[4*N][26]来存储线段数节点
setv[4*N]来存储当前区间覆盖的字母
cnt[26]表示每个字母在当前区间出现的次数
下面以样例“abacdabcda”为例进行阐述:
首先是建树过程:
这一部分计算出sum[4N][26]和setv[4N]的值
其次,对于每一个查询,“l,r,ch”
都要用query(int rt,int L,int R,int l,int r,int i)计算[l,r]区间cnt[i]的值。
最后一步,对于每一个查询”l,r,ch”
根据ch的值:
做update(int rt,int L,int R,int l,int r,int i)
如果ch=0,对[l,r]降序
做法是:对cnt[i]数组从25->0 遍历,若cnt[i]>0,表示这个字符存在,且个数为cnt[i]个
所以要把[l,l+cnt[i]]个位置都要修改为cnt[i]对应的字母,并把l(当前位置后移cnt[i]位)
如果ch=1,对[l,r]升序
做法相同,只是从[0->25]遍历。