• loj6519 魔力环


    解题思路

    考虑顺时针旋转 (i) 步得到的结果,根据Burnside引理,有

    [Ans=frac{sumlimits_{i=0}^{n-1}C(i)}{n} ]

    (C(i)) 为旋转 (i) 步时不动点的数量。

    实际上,旋转 (i) 步得到的是 (frac{n}{gcd(n,i)}) 个互不相干的环,每个环都是一个置换。不难发现,若一个方案在旋转 (i) 步的情况下为不动点,那么对于分割出来的每个小环,要么都为黑色,要么都为白色。

    可以得到,对于 (gcd(n,i)) 相等的 (i) ,它们对答案的贡献是一样的。

    所以可以枚举每个小环的长度 (l) 。不难发现 (l) 需要是 (n) 的因数。而由于每个小环要么全黑,要么全白,所以黑点数量应当为 (l) 的整数倍。即 (l|gcd(n,m))

    所以答案可以写成这样:

    [Ans = frac{sumlimits_{l|gcd(n,m)}f(l)phi(frac{n}{l})}{n} ]

    用到了使用Burnside的时候常用的东西:

    [sumlimits_{i=0}^{n-1}gcd(i,n)=sumlimits_{d|n}sumlimits_{i=1}^{frac{n}{d}}[(i,frac{n}{d})=1]=sumlimits_{d|n}phi(frac{n}{d}) ]

    接下来就要解决如何求 (f(l)) 。可以这样考虑:

    对于环长 (l)(n) 个点被分成了 (frac{n}{l}) 个环。不妨将每个点以其所在的点标号,那么就会产生类似于 (1,2,3,1,2,3) 这样的重复。也就是说,还可以将“ (1,2,3) ”看成 (1) 个环,总共 (l) 个环,每个环 (frac{n}{l}) 个点。为了方便叙述,不妨将其称为“第二种环”。

    每个“第二种环”恰好有“第一种环”上每个环里一个点,也就是说,恰好有 (frac{m}{l}) 个黑点。即需要在一个 (frac{n}{l}) 元环上选 (frac{m}{l}) 个黑点,其中连续的黑点不超过 (k) 个。注意这里的环相当于有了标号,旋转相同的不算重复。

    可以考虑断环成链,然后枚举链首尾连续的黑点个数之和 (j) 。如果记 (x) 个黑点分成 (y) 份,每份可以为空的方案数为 (F(x,y)) ,那么环上的答案就是:

    [f(l)=sumlimits_{j=0}^k(j+1)F(frac{m}{l}-j,frac{n}{l} - frac{m}{l} - 1) ]

    (F) 的值可以通过容斥得到

    [F(x,y)=sumlimits_{i=0}^{i * (k+1) leqslant x, i leqslant y}(-1)^i{ychoose i}{x - i * (k + 1) + y - 1choose y - 1} ]

    计算 (F(x,y)) 的时间复杂度是 (O(frac{x}{k})) 的,计算 (f(l)) 的时间复杂度是 (O(frac{n}{l})) 的。所以总的时间复杂度是 (O(n)) 的。

    参考程序

    #include <cstdio>
    
    #define Maxn 100010
    #define Mod 998244353
    
    int Fact[Maxn], Inv[Maxn], Phi[Maxn], Vis[Maxn], Prime[Maxn], n, m, k;
    
    inline void Pre();
    inline int f(int x);
    inline int F(int x, int y);
    
    #define Add(x,y) ((x)+(y))%Mod
    #define Dec(x,y) (x)>(y)?(x)-(y):(x)-(y)+Mod
    #define Mul(x,y) 1ll*(x)*(y)%Mod
    inline int Pow(int x,int y){int A=1;for(;y;y>>=1,x=Mul(x,x))if(y&1)A=Mul(A,x);return A;}
    #define C(x,y) 1LL*Fact[x]*Inv[y]%Mod*Inv[(x)-(y)]%Mod
    
    int main() {
    	scanf("%d%d%d", &n, &m, &k);
    	if (k == 0) return printf("%d
    ", m ? 0 : 1), 0;
    	if (m == 0) return printf("1
    "), 0;
    	Pre();
    	int gcd = [](int x, int y)->int{int T = x % y; while(T) x = y, y = T,T = x % y; return y; }(n, m);
    	int Ans = 0;
    	for (int i = 1; i <= gcd; ++i) 
    		if (!(gcd % i)) 
    			Ans = Add(Ans, Mul(f(i), Phi[i])); 
    	Ans = Mul(Ans, Pow(n, Mod - 2));
    	printf("%d
    ", Ans);
    	return 0;
    }
    
    inline void Pre() {
    	Fact[0] = 1; for (int i = 1; i <= n; ++i) Fact[i] = Mul(Fact[i - 1], i);
    	Inv[n] = Pow(Fact[n], Mod - 2); for (int i = n - 1; i >= 0; --i) Inv[i] = Mul(Inv[i + 1], i + 1);
    	Phi[1] = 1;
    	for (int i = 2; i <= n; ++i) {
    		if (!Vis[i]) Phi[i] = i - 1, Prime[++Prime[0]] = i;
    		for (int j = 1; j <= Prime[0] && 1LL * i * Prime[j] <= n; ++j) {
    			Vis[i * Prime[j]] = 1;
    			if (i % Prime[j] == 0) { Phi[i * Prime[j]] = Phi[i] * Prime[j]; break; }
    			Phi[i * Prime[j]] = Phi[i] * (Prime[j] - 1);
    		}
    	}
    	return;
    }
    
    inline int f(int l) {
    	if (n / l - m / l == 1) return (m / l <= k) ? m / l + 1 : 0;
    	int Ans = 0;
    	for (int j = 0; j <= k; ++j)
    		Ans = Add(Ans, Mul(j + 1, F(m / l - j, n / l - m / l - 1)));
    	return Ans;
    }
    
    inline int F(int x, int y) {
    	int Ans = 0;
    	for (int i = 0; i * (k + 1) <= x && i <= y; ++i) {
    		if (i & 1) Ans = Dec(Ans, Mul(C(y, i), C(x - i * (k + 1) + y - 1, y - 1)));
    		else Ans = Add(Ans, Mul(C(y, i), C(x - i * (k + 1) + y - 1, y - 1)));
    	}
    	return Ans;
    }
    
  • 相关阅读:
    Ansible 详细用法说明(一)
    Puppet基于Master/Agent模式实现LNMP平台部署
    推荐-zabbix原理篇
    Centos 6.x 安装Nagios及WEB管理nagiosql实现windows及linux监控指南
    CentOS 7下安装Logstash ELK Stack 日志管理系统(下)
    【Python基础学习二】定义变量、判断、循环、函数基本语法
    【Python基础学习一】在OSX系统下搭建Python语言集成开发环境 附激活码
    内联函数
    2016 科大讯飞校招研发一面二面 10.13
    hiho #1151 : 骨牌覆盖问题·二 (递推,数论)
  • 原文地址:https://www.cnblogs.com/chy-2003/p/12010125.html
Copyright © 2020-2023  润新知