分类目录归档:acm

[POJ 3678]

Katu Puzzle

很神奇的2-SAT问题。找了一篇赵爽的《2-SAT解法浅析》。作者是高中生啊,查了下却发现是05年交大ACM总冠军队的成员……Orz

题目抽象出来后就不难做了,建一个含2*N个点的有向图,边由逻辑运算给出,边(u,v)表示选了u点则必须选v。编号为i的点对应图中i*2和i*2+1的两个点,分别表示i点取值为1、0。点A、B间由逻辑运算生成的边如下:

A & B == 1: 增加边(A*2+1,A*2)和(B*2+1,B*2);

A & B == 0: 增加边(A*2,B*2+1)和(B*2,A*2+1);

A | B == 1: 增加边(B*2+1,A*2)和(A*2+1,B*2);

A | B == 0: 增加边(A*2,A*2+1)和(B*2,B*2+1);

A ^ B == 1: 增加边(A*2,B*2+1)、(B*2+1,A*2)、(A*2+1,B*2)和(B*2,A*2+1);

A ^ B == 0: 增加边(A*2,B*2)、(B*2,A*2)、(A*2+1,B*2+1)和(B*2+1,A*2+1);

建好图后求强连通分量。最后判断是否对所有的i=0:N-1,是否有i*2和i*2+1不在同一强连通分量中,若成立则原问题有解,否则无解。

这里被 A & B == 1 和 A | B == 0 卡了一下……不可取的点直接引入必败边的做法非常巧妙……

同样分别使用了两次DFS和tarjan来求强连通分量,代码分别如下:

[codeforces] 杀入Div. 1!

各种磕绊过后……终于我也Candidate Master啦!附送链接:eurce01

从#184开始……一场没算分,一场C题sort用了greater_equal导致TLE,一场电脑被A没做之后……终于我也Rating 1742啦!!

其实Div. 2的题还是挺轻松的……各种水题虐虐也能加分,不过也是#189开始才过了D题,结果直接Rank 17了。

但貌似这种难度的题才相当于Div. 1的B题啊……目测接下来还是狂跪的节奏……

无论如何还是bless自己一下吧……

[POJ 2886]

Who Gets the Most Candies?

还是线段树……题目是N个人站成一个环进行N轮的筛人,每轮都筛走一个人,第i轮筛走的人得到f(i)颗糖,要求最先得到最多糖的人。第一个被筛的人为k,每次某人被筛,都会给出下一个被筛的人相对当前位置的偏移量(顺时针方向)ai,ai!=0且|ai|<10^8。 其实知道要用线段树之后,题目就很简单了……k值的维护可以通过剩余人数n-i和a[i]求出,然后每轮筛去还在环中的第k个人即可,用线段树可以log(n)得到每轮被筛的人的编号。但这题竟然卡的是求f(i)。。。换了两种方法求f(i)都超时后便老老实实打表了。。。

[POJ 2777]

Count Color

依然是线段树,和POJ 2528比较类似,增加了查询某段区间内颜色种类数目的要求,这里由于颜色总数T值比较小(1<=T<=30),因此可以用一个int以位相量的形式来表示某段区间包含的颜色情况。这里lazy tag用来表示结点的区间是否只含一种颜色。

用C++最快也只有266ms,但代码量只有第一名a3616001(79ms)的一半……这个可以用作自我安慰吗?

[POJ 2528]

Mayor’s posters

很基础的线段树题目。在范围[1,10000000]内依次覆盖n段区间[ai,bi],问最后未被完全覆盖的区间共有多少个。

做的时候参考了http://bbs.byr.cn/#!article/ACM_ICPC/71462。关键是对每个结点要设置tag记录是否为叶子,每次拆分时,叶子结点的值继承被拆结点的值,并将tag设置为1。插入每段区间时,根据区间和结点之间的上下界关系进行相应处理即可。最后全部插入完毕后,用DFS查找值有效的叶子结点种类数目:

[POJ 3308]

Paratroopers

典型的最小割模型题目。做之前先读了国家集训队论文《最小割模型在信息学竞赛中的应用》。

题目大意是,有一个m*n的网格,m行和n列各对应一个权值。网格里面有l个点,如何在m行和n列中选择某些行列,使所有的点被覆盖,且使行列权值之积最小。

建图时S连所有行,E连所有列,行和列之间的边则对应原图的点且容量为无穷。这样便能应用最小割模型,得到的点集划分中割边必定为有穷边,且对应着原图中选中的行或列,使原图中每个点都能被覆盖(不能同时到S、E可达)。这里由于所求是作乘,将其取log,便转换成加法运算了。用EK求得最大流后,再从S点出发作DFS,就得到了实际的最小割方案,再依次检查遍历情况作乘即可。

不知为何c++竟然没有log2,CE了一次…T__T

[POJ 3352] and [POJ 3177]

Road Construction

Redundant Paths

两题几乎是一样的,除了数据范围有些许差异。题目大意是,有一个连通的无向图,求至少需要添加多少条边,才能使得在图中任意移去一条边后仍然连通。

(又)WA了一天后发现:可以先缩点,将图转换成一颗树,然后求树上叶子结点的数目cnt,如果cnt==1输出0,否则需要增加的边数即为(cnt+1)/2。另外由于是无向图,不需要用入栈标记数组来判横向边,且已知图是连通的,DFS调用一次即可,略为简化了些代码。

具体实现如下:

[POJ 2186]

Popular Cows

题目大意:一个有向图,边(i,j)表示牛i认为牛j受欢迎,这种关系具有传递性。问:图中存在多少只牛被其他所有牛认为是受欢迎的?

很喜欢这种题……简洁的题面和清晰的题意,但足够一想想一天的。

思路是求强连通分支后缩点,再判断缩点后是否存在唯一出度数为0的点(也即最受欢迎牛群k),然后再判在反向图中,是否其余缩点从点k出发都可达。这里有个坑:如果总共只有一只牛,直接输出0。

具体实现上,分别尝试使用了CLRS上的两次DFS和tarjan来求强连通分支。另外,这题也是第一次写缩点,用了并查集,感觉有点累赘。

两次DFS:

tarjan(总感觉自己敲的东西有种山寨感?):

两种算法的耗时相近,但是两次DFS怎么会和一次DFS的tarjan差不多呢?实在不太明白…

[POJ 2942]

Knights of the Round Table

最近的5天都在研究这一道题目,貌似效率有点低……不过还好最后AC了,作为充实blog的一点体会,也在此稍作记录吧。

一开始是写了一下穷搜,TLE后开始各种想……发现是知识储备不够,于是开始各种读解题报告,随即就读到了双连通分量,tarjan,二分图染色等一系列没听过的东西。BLOG的一个坏处是,知识点总是讲一点不讲一点,甚至有些是直接代码一帖完事。但这篇写得还是挺好的http://blog.csdn.net/lyy289065406/article/details/6756821。

稍微说点题外话,现在做题有个习惯是希望尽量把题目涉及的背景知识面都大致了解一下。之前也把CLRS看了一遍,就是希望自己的知识点不要有太明显的盲点,否则做题时效率实在太低了。但在做有些题的时候,往往认真读了相关定理及证明并实现了书上某个算法后,看题目的discuss却依然一大堆陌生名词。最明显的做网络流时,按照书上写的实现了Edmonds-Karp和Relabel-to-Front算法,也成功解出题目,以为可以和各路神牛做些基本沟通了,结果discuss里一堆dinic、sap什么什么的…………也不知道别人都看的是什么资料。这次这题也有这种感觉,而且真的是不喜欢那种遇到问题再去各种google的方式,感觉就是在打没准备的仗,而且长久下来,知识结构也会变得很零散……刚好这位博主有列出参考书目,也正是大名鼎鼎的黑书了。之前没有选择此书,一是觉得偏竞赛的书籍会较功利,而自己已经不可能全力准备什么比赛了,二是这本书稍微有点老了。但有书看总比一个个BLOG的翻要来得好,还是买回来了。

于是认真读了黑书P285页开始的部分,了解了DFS原来还有各种用法:求连通分量的割点、桥等等。作为对算法思想理解的验证,先A了1523,一道求割点对应分量数目的题目。然后又读了题解中用二分图性质判断奇圈的定理,便开始了各种构思算法实现。这里主要需要解决的问题是,如何保存用DFS找出的双连通分量。一开始我考虑的是在DFS的同时用栈记录当前遍历过的结点,在找到双连通分量时退栈输出结点,WA了数次后发现思路有误。关键在于像:

这样的数据,栈中会存在不属于当前双连通分量(属于父结点对应的双连通分量)但夹在本双连通分量结点间的结点(好罗嗦……)。于是只能用栈存边,这样出栈的时候,便肯定能得到属于本双连通分量的全部结点了。最后,再考虑了一下具体实现,总算是敲出了这题:

虽然AC,但是g++跑了1100ms++,status上神牛们则是100ms左右……估计是递归验证二分图的那步效率太低了,但一时也想不到怎样改进比较好(而且实在不喜欢看别人的代码……)。以后有新思路了再来改进吧~