• 【ybt金牌导航8-5-1】【luogu P4980】彩色项链1 / 【模板】Pólya 定理


    彩色项链1

    题目链接:ybt金牌导航8-5-1 / luogu P4980

    题目大意

    一个项链,然后有 n 个颜色让你涂在 n 个珠子上。
    (不一定要用完所有颜色)
    然后问你有多少种项链本质不同。

    本质不同要两个项链无论怎么选择都不能重合(即对于位置颜色相同)。

    思路

    我们看到本质不同,然后它又是把一些状态转移,自然会想到 Burnside 引理和 Polya 定理。

    它是干嘛的呢?
    就是针对这种状态可以转移的东西,然后能转移就表示本质相同,它就是用来求有多少本质不同的状态。

    然后能使得状态转移的东西,就叫做置换。
    (f=(a_1,a_2,...,a_n)) 这个置换,表示 (i) 可以转移到 (a_i)

    然后你会知道按着这么搞,会出现一个或多个循环,就是某个点可以一直在这个循环里面的跳。
    那这些循环之间是不相交的。

    Burnside 引理就是可以解决有多个置换时能有的本质不同状态个数。
    这里先给出 Burnside 引理的公式:
    (L=dfrac{1}{|G|}sumlimits_{rin G}c_1(r))

    (G) 就是置换群,包含了给出的所有置换。(c_1(r)) 就是在置换 (r) 中自己直接转移到自己的状态个数。

    至于这个证明,我太菜了,证不了。用就完事了!

    接着来看 Polya 定理,就是这道题要用的算法。
    它就是可以处理什么多种颜色的情况。

    在前面的引理中,你需要列出所有的状态,但是如果颜色一多,你就不能表示出所有状态,就需要用这个算法了。

    公式是这个:
    (L=dfrac{1}{|G|}sumlimits_{rin G}m^{c(r)})

    这里 (c(r)) 的意义不再一样,就是 (r) 置换中循环的个数。
    它主要就是说每个循环都可以选任意颜色,然后循环中的点的颜色是相同的,然后中间就变成了这样。

    (m) 是颜色的个数。

    然后你列出置换 (r_k)((k+1,k+2,...,n,1,2,...,k))
    可以知道 (c(r)=gcd(k,n))(|G|=n)

    但是还是不是很优,我们考虑继续优化。
    (L=dfrac{1}{n}sumlimits_{d|n}(n^dsum[d=gcd(k,n)]))
    (因为是 (gcd),所以我们可以把 (gcd) 同一个值的提取出来,前面的求和是单个的贡献,里面的是求出有多少个同一个值的)
    (L=sumlimits_{d|n}(n^{d-1}sum[1=gcd(dfrac{k}{d},dfrac{n}{d})]))
    那你会发现 (gcd) 的右边是跟里面的循环没有关系,而且是要 (gcd) 里面的两个互质,自然想到欧拉函数。
    (L=sumlimits_{d|n}(n^{d-1}varphi(dfrac{n}{d})))

    然后就可以做了。

    代码

    ybt

    #include<cstdio>
    #define ll long long
    
    using namespace std;
    
    int T, n;
    ll mo, ans, su[35001];
    bool ns[35001];
    
    void get_su() {//欧拉筛筛出素数
    	ns[1] = ns[0] = 1;
    	for (int i = 2; i <= 35000; i++) {
    		if (!ns[i]) {
    			su[++su[0]] = i;
    		}
    		for (int j = 1; j <= su[0] && i * su[j] <= 35000; j++) {
    			ns[i * su[j]] = 1;
    			if (i % su[j] == 0) break;
    		}
    	}
    }
    
    ll ksm(ll x, ll y) {//快速幂
    	ll re = 1;
    	while (y) {
    		if (y & 1) re = (re * x) % mo;
    		x = (x * x) % mo;
    		y >>= 1;
    	}
    	return re;
    }
    
    ll phi(ll now) {//算phi值
    	ll re = now;
    	for (ll i = 1; su[i] * su[i] <= now; i++)
    		if (now % su[i] == 0) {
    			re = re / su[i] * (su[i] - 1);
    			while (now % su[i] == 0) now /= su[i];
    		}
    	if (now != 1) re = re / now * (now - 1);
    	return re;
    }
    
    int main() {
    	get_su();
    	
    	scanf("%d", &T);
    	for (int times = 1; times <= T; times++) {
    		scanf("%d %lld", &n, &mo);
    		
    		ans = 0;
    		for (int i = 1; i * i <= n; i++) {//枚举因子
    			if (n % i == 0) {
    				//我们上面弄出来的公式
    				ans = (ans + ksm(n, i - 1) * phi(n / i) % mo) % mo;
    				if (i * i != n)
    					ans = (ans + ksm(n, (n / i) - 1) * phi(n / (n / i)) % mo) % mo;
    			}
    		}
    		
    		printf("%lld
    ", ans);
    	}
    	
    	return 0;
    }
    

    luogu

    #include<cstdio>
    #define ll long long
    #define mo 1000000007
    
    using namespace std;
    
    int T, n;
    ll ans, su[35001];
    bool ns[35001];
    
    void get_su() {
    	ns[1] = ns[0] = 1;
    	for (int i = 2; i <= 35000; i++) {
    		if (!ns[i]) {
    			su[++su[0]] = i;
    		}
    		for (int j = 1; j <= su[0] && i * su[j] <= 35000; j++) {
    			ns[i * su[j]] = 1;
    			if (i % su[j] == 0) break;
    		}
    	}
    }
    
    ll ksm(ll x, ll y) {
    	ll re = 1;
    	while (y) {
    		if (y & 1) re = (re * x) % mo;
    		x = (x * x) % mo;
    		y >>= 1;
    	}
    	return re;
    }
    
    ll phi(ll now) {
    	ll re = now;
    	for (ll i = 1; su[i] * su[i] <= now; i++)
    		if (now % su[i] == 0) {
    			re = re / su[i] * (su[i] - 1);
    			while (now % su[i] == 0) now /= su[i];
    		}
    	if (now != 1) re = re / now * (now - 1);
    	return re;
    }
    
    int main() {
    	get_su();
    	
    	scanf("%d", &T);
    	for (int times = 1; times <= T; times++) {
    		scanf("%d", &n);
    		
    		ans = 0;
    		for (int i = 1; i * i <= n; i++) {
    			if (n % i == 0) {
    				ans = (ans + ksm(n, i - 1) * phi(n / i) % mo) % mo;
    				if (i * i != n)
    					ans = (ans + ksm(n, (n / i) - 1) * phi(n / (n / i)) % mo) % mo;
    			}
    		}
    		
    		printf("%lld
    ", ans);
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    linux下安装nginx
    阿里云CentOS系统配置iptables防火墙
    没用私服,自己安装下本地jar包,制作坐标
    Redis加入Centos Linux开机启动
    CentOS6.5安装redis(3.0.3)
    Spring Boot 第一个demo
    JDK下载与安装
    IDEA-Maven下载及安装
    项目注意:
    idea项目Tomcat 插件 运行报错
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_8-5-1.html
Copyright © 2020-2023  润新知