查看文章 |
原题可以在HDU上找到:http://acm.hdu.edu.cn/showproblem.php?pid=2481 诚然,这是一道非常难的题目,当时在现场只有清华的陈丹琦那个队出了这道题,他们是唯一一个出了7道题的队伍。
最后的结果是,这道题至少浪费了我们20分钟。因为我们不熟悉高幂取模的写法... ...当时我们那叫一个菜.....算了 后来我们找清华的队问这道题的解法,那个男生大致说了一下,我当时不懂,所以记住的不多。隐约记得的几个词语是:把N分解因子,枚举公约数,Burnside引理。 其实以我们当时的实力,这题不是我们该问的。 今年我学习了大量的新知识后,终于于昨天把它A掉了,下面与大家分享一下这一题的想法: 这一题有两个难点,一个是如何统计不可旋转时,合法的方案总数。方法是先假象一个动态规划算法,然后找出递推公式,再表示成矩阵的幂。 另一个难点就是通过枚举循环节,利用欧拉函数快速统计所有置换中不变方案的总和。这一点本来是比较难的,但是如果做过楼天成的Color那个题,这个技术就可以依葫芦画瓢了。 如果你没做过楼天成的color那个题,我建议你先做一下color再来做这道题,这个Toy比color难,color在http://acm.pku.edu.cn/JudgeOnline/problem?id=2154 ,是Polya计数,枚举统计的部分和这题一模一样。唯一的不同时Toy这题的染色是带有限制的,需要另想办法计算确定了循环节之后的方案数,而不是简单的N^k。 我们来看看假如图形不允许旋转,如何统计不同的方案总数。 在图上选固定的A、B两个相邻的点。假设AB两个点之间没有边相连,我们设整个图形中有节点数为n(不包括中间点),AB之间没有边相连的方案总数为 f[n]。枚举A往右边数连接的最多的节点数,假设这个数量是k(如图),把这K各节点去掉,由B到C之间可以看成一个新的图形,总共有n-k各节点,BC之间断开(是指沿弧BAC方向,B不与C直接相连),显然这个新图形内部的连接方案总数为f[n-k]。 再考虑到从A到k这k各节点一定有且只有一个与中心相连,因此本身有k种接法,所以如果A往右边连续连接了k各节点,那么这个连接方案总数是f[n-k]*k。 再枚举k,得到: f[n]=f[0]*n+f[1]*(n-1)+f[2]*(n-2)+f[3]*(n-3)+.....+f[n-1]*1. 规定f[0]=1。因为f[0]*n这一项表示从外围除了AB之间全部连上的情况,恰有n种,所以规定f[0]=1是合理的。 设s[n]是从f[0]到f[n]的总和,那么上式可以写为: f[n]=s[n-1]+f[0]*(n-1)+f[1]*(n-2)+f[2]*(n-3)+....f[n-2]*1 =s[n-1]+f[n-1]=s[n-2]+2*f[n-1] = (f[n-1]-f[n-2])+2f[n-1] =3*f[n-1]-f[n-2], 注意变换的过程中认为s[n-2]=f[n-1]-f[n-2]是因为第二部中已经发现f[n]=s[n-1]+f[n-1]。由于n是指任意自然数,所以这个结论对于f[n-1]也成立。 到这一步,我们就已经可以通过求矩阵的幂来计算f[n]了,考虑到f[1]=1,f[2]=3,还可以进一步得出f[n]=Fibonacci[2n-1]。 f[n]是指A、B不相连的连接方案数。考虑A、B相连,同样,我们设与AB连续相连的节点有k个(如图)
我们设总共有n个节点,AB相连的方案数为g[n]。如图,图中从1到k共k各节点是连续相连的,1与1左边那个不相连,k与k右边那个不相连,AB是这一段边上相邻的两个点。因为AB包含在1...k中,所以这条边的位置有k-1种选择;同样,这条边中有且只有一个与中心相连,从而本身有k种选择;除去1...k剩下的那一部分有n-k各节点,本身有f[n-k]种选择。 所以,图形中A、B两点相连,并且与A、B连续相连的节点有k个时,排列的方案数为(k-1)*k*f[n-k],枚举k,得到: g[n]=(2-1)*2*f[n-2]+(3-1)*3*f[n-3]+......((n-1)-1)*(n-1)*f[1]; 为了得出g[n]与f[n]的关系,计算g[n]-g[n-1]: g[n]-g[n-1]=2*f[n-2]+4*f[n-3]+6*f[n-4]+....2*(n-2)*f[1] =2*f[n-1] 从而: g[n]=2*(f[1]+f[2]+f[3]+....f[n-1]). f[n]是可以通过计算矩阵的幂得到的,设这个矩阵是A,A={{2,1},{1,1}},计算A+A^2+A^3+.....A^(n-1)就可以得到 f[1]+f[2]+f[3]+....f[n-1] 。如果你不清楚怎样快速计算A+A^2+A^3+.....A^(n-1),去看一下这个题:http://acm.pku.edu.cn/JudgeOnline/problem?id=3233 现在已经有办法快速计算如果不允许旋转,合法的方案总数了。题目中说了旋转之后算相同的方案,怎么办呢?当然是使用Burnside引理,总共n个置换,统计每个置换下保持不变的方案总数之和,再除以n就可以了。假设置换c[i]表示将整个图形顺时针转过i个节点,那么置换i的循环节总数就是gcd(i,n),每个循环的长度是n/gcd(i,n)。对于循环节总个数是k,是每个循环节都相等的连接方案总数 与 总共有k个节点,连接不受限制的连接方案总数是相同的。因为你可以假想在整个图形中截取一小段长为gcd(i,n),然后把他们首尾相连就构成了一个新的有gcd(i,n)个节点的图形,它的连接方案总数与原来的整个图形把每个循环节都置成相同的连接方案总数是一致的。 n<=10^9,我们不能逐一统计每个置换的gcd(i,n)值,好在循环节长度为gcd(i,n)的置换总数量,就是(n/gcd(i,n))的欧拉函数。按照这个方法枚举gcd(i,n)就可以快速统计所有置换中保持不变的方案总数之和了。这一部分的具体的细节可以参考POJ2154,就是刚才说了楼天成的color那个题,color到处都有解题报告(但是Toy的解题报告就不好找了,所以我决定写一份)。 基本思路就是这样的,最后在取模的环境下除N,且不保证MN互素,我的办法是先把一开始就把M乘上N,这样最后的答案就能直接除N了,附上代码: #include<iostream>
顺便说一下,这份代码在HDU上跑得特别快,A掉此题的另外三个人中有两个分别跑了7000多ms和6000多ms,最快的acrush也跑了5000多ms,而这份代码只要400多ms,快了10倍多。我不知道别人用的什么算法,我觉得统计不旋转的方案总数可能有更简单的算法,我这种分析真是太麻烦了。 在现场赛中清华它们应该用了更好的办法,它们讲的难点似乎主要是枚举循环节的一部分。如果在时间紧迫的的现场赛,统计不旋转的方案总数用我这种方法,那真是太恐怖了。希望清华他们也发一份解题报告,分享一下他们更为巧妙的方法
|



