集训期间完成6道,补题1道。
比赛链接:http://vjudge.net/contest/121496#overview
A - Lights Against Dudely(状压+暴力)
题意:
给定$n*m$的矩阵,其中$k(0 \le k \le 15)$个地方可以放灯,每个灯只能照到右边和上边的两个方格,有一种特殊的灯,可以旋转。灯只能照到有灯的地方,问最少需要多少盏灯?
分析:
注意审题!最多只有$15$个灯,和一盏特殊的灯,吃果果的状压枚举,然后就没了。
注意$vis$数组初始化用$memset$会T啊,我真是T了几百年,改了就A了,可能也跟我代码写的很挫有关。。
罗大神说可以用一种类似时间戳的方式,这样不必每次都初始化,只要判断并不断更新他的时间戳即可。
代码:
|
|
B - Stealing Harry Potter’s Precious(BFS + DFS)
题意:
给定一个$n*m$的地图,及$k(k \le 4)$个点,问从起点开始全部经过这$k$个点的最小步数。
分析:
与正常$bfs$不同的是这题要求必须走$k$个点,而$k \le 4$,那么我们可以先$bfs$求出任意两点之间的最短距离,再枚举这$4$个点的走的顺序,把距离加起来即可。
最初我的做法是,先$bfs$找到$k$个点与出发点以及$k$个点之间的最短路,然后$dfs$找到最短的一条路,将最短路连接在一起。
代码:
|
|
状压
C - Zhuge Liang’s Password(模拟)
题意:
给定两个$n*n$的矩阵,矩阵可以进行$90,180,270$的旋转,问两个矩阵完全重合的时候最多有几个数是相同的。
分析:
这场最水的一道题了。。暴力模拟一下即可。
代码:
|
|
F - Infinite Go(并查集,模拟)
题意:
两个人下围棋,问最后黑白棋子各剩多少。
分析:
模拟,第一反应连通块可以用并查集搞,然后就顺着题意一直在纠结怎么判断联通块内部的空白区域的大小,其实转换个角度,我们可以看一个连通块周围是否有空白区域,如果一个连通块四周被相反颜色的包围,没有剩余空白区域,那么整个连通块都要被移走。这样我们只要维护连通块周围的空白部分即可。删除的时候dfs遇到颜色相同的就删除,否则该块所在连通块的空白区域加一。
然后就狂T不止,因为$map$常数太大,每次找颜色都访问一次$map$的话,太耗时,直接作为参数传进$Delete()$中
代码:
|
|
G - Ants(01Trie树)
题意:
$n$个点组成一棵树,给定树边长度,定义任意两个不同点之间的距离为两点之间边的异或值(考虑顺序),$m$个询问,求所有距离中第$k$大距离为多少。
分析:
首先利用异或的性质,我们可以随便找一点作为根节点,然后$dfs$求出每个点到该点之间边的异或值,那么求两点之间的距离就将两点的$dist$数组异或一下即可~
这样我们就可以将$dist$数组插入$01Trie$树中,那么对于单独的一个点,问题就可以转化为经典的求异或值第$k$大的问题了。
但是如何处理所有点的第$k$大呢?这里就开始想不清楚了。。。。
======= 我是智障的分界线 =====
我们将询问离线处理。。
直接得到第$k$大不好算,可以从第1大开始,依次计算。
首先将所有点的第$1$大进行比较,选出最大,然后将选出的最大值删除,并将其对应的点的第$2$大值,加进去再与那些第$1$大们进行比较选出第$2$大值。。。依次类推。。这样是可以保证我们每次取到的点时全局最大,第$2$大。。等等。。。而比较的过程我们可以使用优先级队列,每次取队首元素即可。。。。
代码:
|
|
H - Rabbit Kingdom(树状数组)
题意:
给定序列,若干询问,求给定询问区间中互质的数的个数。
分析:
智商太低理解了好久好久,最后还是看别人代码明白的。
设$l[i]$为位置为$i$的元素左边第一个与$w[i]$不互质的数,$r[i]$为右边第一个与$w[i]$不互质的数,那么$(l[i], r[i])$区间内的所有数均与$w[i]$互质。
假设我们从头开始扫,为了防止重复计算,我们不管$i$左边的数,那么每遇到$i$,就把$i$的贡献加到后面相应的区间$[i,r[i])$里,实现起来我们就可以用树状数组(或线段树)维护区间互质数的对数和,每遇到$i$,就$add(i,1),add(r[i],-1)$。
下面处理询问,首先对所有询问根据区间左端点进行排序,然后我们从头开始扫,设头指针$pp=1$,每次遇到$l[i]=pp$时,说明对于左端点在$pp$之后的询问他是有贡献的,就把他的贡献加到相应的区间中,即$[i, r[i])$,指针扫过时再进行还原。我们可以用$vector$保存每个$l[i]=pp$的元素位置$i$,然后每次访问到一个位置直接遍历相应的$vector$即可。
关键是
- 区间问题,前缀和不可搞,就用两个数组标记头和尾
- 只考虑右区间,当到达左区间时,说明他的贡献算数,就把他的贡献加上。
代码:
|
|
I - Gems Fight!(博弈+DP)
题意:
有$B(0\le B \le 21)$个背包,有$G(0 \le G \le 8)$种颜色的宝石,这两个人轮流选择某一个背包,把这个背包包里的宝石放到一个共享的地方里,当这里某一种颜色的宝石等于$S$,那么就可以产生一个魔法石,这个人得到这个魔法石并且还能得到一个额外的拿包的机会,两个人都用最佳策略,问最后两个人能获得的魔法石的差。
分析:
- 首先可以明确,背包最多只有$21$个, 所以我们可以用状态压缩来表示拿背包的情况。
- 对于不同局势,先手后手均采取最优策略,所以二者均采取同样的方式拿包。
- 另外一个重要性质就是两个人拿到魔法石的总数总是确定的
那么我们就可以得出:
设$dp[i] :=面对背包状态为i时可以获得的最大的魔法宝石数$
状态转移方程:
如果拿某个包产生了新的魔法石,则可以继续下一轮,那么dp[i] = max (dp[i], dp[i|(1<<j)] + ccnt);
否则,换手,那么$dp[i|(1<<j)]$就是下一手的了,根据总和我们就可以求出当前手对应的值dp[i] = max (dp[i], res - dp[i|(1<<j)]);
当所有背包都被取即$i == 1 << n - 1$时,此时获得的最大魔法石数为$0$,那么我们倒着推一遍,先手获得的最大值即为$dp[0]$。
然后位运算少加个括号就成功调试了一个小时。
代码:
|
|