比赛地址:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=30596#overview
解题报告:
(来自杭电ACM微博)
1001
注意到任意一个回文子序列收尾两个字符一定是相同的,于是可以区间dp,用dp[i][j]表示原字符串中[i,j]位置中出现的回文子序列的个数,有递推关系:
dp[i][j]=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]
如果i和j位置出现的字符相同,那么dp[i][j]可以由dp[i+1][j-1]中的子序列加上这两个字符构成回文子序列,也就是
dp[i][j]+=dp[i+1][j-1],注意边界特判一下就可以了
1002
明显的poyla,首先枚举出所有的置换群,再算置换群中的不动点的个数就可以了,这里大致的描述一下
首先是连接面中心的置换群,有3个,分别是逆时针旋转90度,180度,270度
还有连接对边中心的置换群,有6个,分别是旋转180度
还有连接对角线顶点的置换群,有4个,分别是旋转120度,240度
至于每个置换群中的不动点大家就自己人肉吧,注意要细心一点……
1003:
注意一个强制拐弯的格子如果强制方向的下一个格子是墙,那么它将永远卡住。将所有格子的四个方向都看做一个状态。枚举处理所有起点和所有钥匙的四个方向出发,到达其他所有格子所有状态的最少滑动数。这个可以BFS一遍,dp[x][y][z]表示到达(x,y)并且方向为z的最少滑动数。处理出来后,就是求起点出发经过所有钥匙最后到达终点的最少滑动数,这可以直接8!或者dp[1<<8][8]求解。
1004
最终添加完边的图,肯定可以分成两个部X和Y,其中只有X到Y的边没有Y到X的边,那么要使得边数尽可能的多,则X部肯定是一个完全图,Y部也是,同时X部中每个点到Y部的每个点都有一条边,假设X部有x个点,Y部有y个点,有x+y=n,同时边数F=x*y+x*(x-1)+y*(y-1),整理得:F=N*N-N-x*y,当x+y为定值时,二者越接近,x*y越大,所以要使得边数最多,那么X部和Y部的点数的个数差距就要越大,所以首先对于给定的有向图缩点,对于缩点后的每个点,如果它的出度或者入度为0,那么它才有可能成为X部或者Y部,所以只要求缩点之后的出度或者入度为0的点中,包含节点数最少的那个点,令它为一个部,其它所有点加起来做另一个部,就可以得到最多边数的图了
1005
首先人肉出在原足球中任意两个面的关系建个图(我觉得我已经很有爱的把比较不好想的关系在hint中都给出了吧……),之后就变成了无向图中的问题了,具体做法是如果两个节点的目标状态相同,他们之间就连权值为1的边,否则连权值为0的边,然后从每个点做一个最短路出去求得到到达每个点的最短路dis[i],如果把当前枚举的起始点作为最后染色的点,那么需要的步数就是最大的dis[i],其中i的颜色必须是黑色,因为起始的状态每个点都是白色的,这样枚举全部的点作为最终的状态过去,选择其中花费最小的就是答案了,下面简要证明一下
要证明这个做法的正确性只需要证明任意一个最优状态都可以转化成套圈圈的染色方法,因为上面的做法是求出套圈圈的染色方法中的最优解,套圈圈的意思就是每次染色的集合除了第一次都是上一次的子集……
现在给定一个任意的染色方法,如果染色的区域是联通的(染色过的全部点只出现在一个连通分支),那么肯定可以按照他们相交的状态调整成套圈圈的染色方案,如果染色的区域不是联通的(不只一个联通分支),那么染色的次数至少是两个,我们可以通过中间一些没有染色的集合把这两个区域连起来,并且按照颜色的种类把染色次数少的那堆染色合并到次数多的那堆染色,最后再多操作一次(也可能是不需要操作的)把中间那个用来连起来的区域染成白色,这样调整的方案一定是不比给定的方案不好的
比如1-2-3这个链,有一种染色情况是染色了1和3,那么我们可以调整成第一步染1,2,3这三个,就是上面讲到的合并两种染色方案,然后把中间分支(2号节点)染色成白色,这样调整的步数一定是不大于原来的步数的
1006
首先根据两个人的速度和第一个人先跑出去的时间,可以算出第一个人在雨中跑的时间,由于第一个人是向左跑,雨是向下落,那么人相对于雨就是向左上角跑了,这样会好处理一点……于是对于每个雨滴,用人所在的线段去和雨滴求相交,计算这个线段在雨中的时间,注意这个线段的两个端点可能在雨滴中,可能和雨滴重叠……这样对于每个雨滴可以算出对应的时间区间,在这个时间区间里面就代表这个人在雨里面,求出全部的时间区间之后扫描线过去求一下这些点段的并的长度就是答案了,注第一个人一开始跑出去的时间也是有可能碰到雨的,在计算代表第一个人的线段的起始端点的时候要加上这段,比赛的时候好多队伍没有注意到这个问题就挂了……
1007:
题意为询问一段区间里的数能组成多少段连续的数。先考虑从左往右一个数一个数添加,考虑当前添加了i - 1个数的答案是x,那么添加完i个数后的答案是多少?可以看出,是根据a[i]-1和a[i]+1是否已经添加而定的,如果a[i]-1或者a[i]+1已经添加一个,则段数不变,如果都没添加则段数加1,如果都添加了则段数减1。设v[i]为加入第i个数后的改变量,那么加到第x数时的段数就是sum{v[i]} (1<=i<=x}。仔细想想,若删除某个数,那么这个数两端的数的改变量也会跟着改变,这样一段区间的数构成的段数就还是他们的v值的和。将询问离线处理,按左端点排序后扫描一遍,左边删除,右边插入,查询就是求区间和。
1008
比较简单的dp,直接用dp[i]表示0-i这段区间可能变成的不同句子数,首先有dp[i+1]+=dp[i],如果当前位置向后连续4个字符是hehe,那么有dp[i+4]+=dp[i],最后的答案就是dp[len]了
1009
首先用一个简单的2^n*n的dp可以求出一个人访问一个给定状态的最小花费,因为这i个人是等价的,所以用dp[i][mask]表示i个人跑完mask这个状态的最小花费,所以首先枚举集合mask,对于dp[i][mask],枚举mask的子集v,dp[i][mask]可以由dp[1][v],dp[i-1][mask^v]转移过来,注意这里用来合并的集合是不能有重复的,这个类似背包……这样就可以算出对于每个状态三个人访问完的最小花费,之后对于给定的需要访问的状态,枚举包含这个给定状态的集合更新答案就可以了……注意对于刚开始给定的图是不能floyd的,因为要求任意两个人的路径不能相交……还有就是1这个节点可以每个人经过多次,这里需要特殊处理一下……
1010:
题意为动态往字符串末尾添加字母,同时在这过程中会询问当前字符串中出现k次以上的不同的子串数。首先,后缀数组处理后扫描一遍,按照height值维护单调栈,这可以处理每一段出现一定次数的子串,一些出现相同次数的子串会在一步中一起考虑。这就能够求解没有添加操作的版本。将字符串倒过来,从末尾添加字母视为从前端添加,离线处理,将所有插入插入到字符串中,打上时间标记。后缀数组处理后,扫描一遍,处理所有出现一定次数的子串,假设总共有m次,每次都有时间标记,即哪个操作添加上的,那么就是看第k小的时间标记,这个操作之后这种子串都会被添加到答案。所以只要求第k小的时间标记,这里可以用划分树预处理,然后对在第k小时间之后的询问都加上当前这些子串数。这可以用树状数组统计,总复杂度n*logn。
比赛时有过了的队伍好像是用后缀自动机,暴力遍历fail指针更新的。
1011:
可以看出,每次翻转都会翻动最右下角的格子。如果右下角刚开始为1,那么先手的人每次都翻动右下角的,使该格子变为0,后手的翻其他矩形,肯定会使得最右下角的格子变为1。这样先手每次都有格子翻,最后肯定是后手败。如果右下角刚开始为0,若没格子翻则后手胜,否则不管翻哪个格子,都会导致右下角变为1,后手每次都翻右下角,肯定必胜,所以是后手胜。所以结论是右下角刚开始为1则先手胜,否则后手胜。
还没有补完,先占坑吧~~