该题果然是个好题啊!
题意来自上一题, ( http://blog.csdn.net/jayye1994/article/details/37814965 ) BZOJ 1002: [FJOI2007]轮状病毒
上一题是旋转后相同视为不同情况,这题旋转后相同视为同一种情况。就这么一个小小的区别,
上一题用到了dp,这一题用到了dp、筛素数、二进制模拟乘法、矩阵、快速幂、欧拉函数、burnside引理。。
同样是人(题)。。做人(题)的差距怎么就这么大呢。。
数据范围:周围是n个点,答案取模m, n<=10^9, m <= 10^9
思路:
要做这题首先是肯定要会burnside引理的,不会的可以去做几题,刘汝佳的训练指南上有。
然后考虑有n种置换,分别是不动、旋转i个单位(i < n)。
首先解决不动的情况,实际上就是轮状病毒要求的结果,可是这里的n<=10^9,那题我计算的复杂度是O(n^2)的,
计算公式也不能快速幂,然后那题的dp公式可以推出dp[i][1] = 3*dp[i-1][1] - dp[i-2][1],推结果ans的话我就推晕了,
那么打个表就可以发现规律。。ans[i] = 3*ans[i-1] - ans[i-2] + 2,于是不动的情况解决了。。。
接下来就是要求旋转的情况了,旋转i个单位,很容易可以得知循环节是n / gcd(n, i) ,循环的个数为gcd(i, n),所以每gcd(i,n)构成一块,每一块都是完全相同的,如下图所示
想了下还是把上一题的dp贴过来,这样子看起来比较清楚。
题目就是求最小生成树的种数,中心的点必须要和周围的一圈点连通,可以先不要管环,先考虑链的情况
dp[ i ][ 0 ]表示第i个点还没和中心点连通,并且前i-1个点和中心点或者第i个点是连通的
dp[ i ][ 1 ]表示前i个点全部都已经和中心点连通了
很容易可以推出状态转移方程 :
dp[ i ][ 0 ] = dp[ i-1 ][ 0 ] + dp[ i-1 ][ 1 ]
dp[ i ][ 1 ] = dp[ i-1 ][ 0 ] + dp[ i-1 ][ 1 ]*2
那么对于n轮状病毒,可以枚举第一个点和多少个周围的点连通,其余的点就是一条链的情况了。
比如说i=2,n=6,那么gcd(2,6) = 2,循环长度是2,也就是说红色线段和绿色线段在重复出现,那么可以知道中间的所有的点都要是连通的,如果两侧的点也都和中心点连通了的话,绿色线段就要删掉,情况数就是dp[ gcd(i,n) ][ 1 ]。求循环长度是d的个数可以用欧拉函数来求,所以不需要枚举i(i<=n),只需要枚举n的因子就可以了。
如果某一侧点没有和中心点连通的话,绿色线段是必要的,情况数就是2*dp[ gcd(i,n) ][0],因为有两侧所以乘2,但是要注意如果这一段所有的点都没和中心点连通的话是不行了,所以情况数还需要减去 2 * 1。
然后问题差不多已经解决了,不过还有一个问题,最后算出的答案还需要除以n,但是n和m不一定互质,所以不能求逆元
所以是求(a/b) mod c,因为知道a/b是一个整数,所以ans = a/b + xc,然后ans * b = a + xbc = a mod (bc),所以可以求出a mod (bc)然后除以b就可以了,中间乘法会爆long long,所以要二进制位上加法来模拟乘法。
code: