zoukankan      html  css  js  c++  java
  • 【ybt金牌导航8-7-2】周期字符串 / 关于莫比乌斯反演的少量知识

    周期字符串

    题目链接:ybt金牌导航8-7-2

    题目大意

    求长度为 n 的只有小写字母组成的循环节长度为 n 的字符串个数。
    循环节是最短的复制若干遍后拼起来跟原串相等的字符串。

    思路

    讲一讲莫比乌斯反演

    莫比乌斯反演其实就是不断的用容斥,而莫比乌斯函数就是用来容斥的容斥系数。

    (varphi(n)=sum_{d|n}mu(d)frac{n}{d}) 作为例子,我们来看看是怎么容斥的。

    首先我们要知道 (varphi(n)) 这个函数原本是干什么的,它叫欧拉函数,是 (1sim n) 中与 (n) 互质的数的个数。

    那首先 (d=1) 的时候,意思就是把所有的数先都塞进答案中,答案加 (n)
    那如果出现了一个 (d) 使得 (gcd(n,d)>1),那 (d) 一定是 (n) 的某个质因数的倍数。
    那你就会想,可以找到每个质因数 (p),然后看能有多少个小于等于 (n) 的倍数,也就是 (frac{n}{p}) 个,然后就给答案减去这个个数。
    当然,你会发现它计算重复了。

    如果两个不同的质因数 (p_1,p_2),它们乘起来肯定是小于 (n),也一定是 (n) 的因子。那如果有 (p_1p_2|d),那这样的 (d) 会被前面的操作中被计算了 (C_2^1) 次,那就多扣了一次,那答案就要加回去,那对于这两个质因子,这样的 (d)(dfrac{n}{p_1p_2}) 个,那就加回 (dfrac{n}{p_1p_2})
    然后你会发现它还是会重复。

    如果三个不同的质因数 (p_1,p_2,p_3),那乘起来还是 (n) 因子。那如果有 (p_1p_2p_3|d),那就被扣除了 (C_3^1-C_3^2) 次,少扣了一次,那为了扣除一次,答案又要扣 (dfrac{n}{p_1p_2p_3})

    然后你会发现,它就是加回去,减回去,加回去,减回去不断进行。

    那你会看到,当你处理的质因数个数是奇数的时候,它就是扣的操作;是偶数的时候,就是加的操作。
    那当质因数个数为 (k),这些质因数乘起来是 (d),那答案就要加上 ((-1)^kfrac{n}{d})
    然后你再看莫比乌斯函数的定义,你会发现它就是 ((-1)^k)。别的时候不管,不需要加减,所以是 (0)(1) 的时候等于 (1) 就是为了一开始的那个全部。

    那就是这样的。

    那就像上面的东西如果你有一个函数 (g(m)=sumlimits_{d|m}f(d)),你 (g(m)) 函数很好求,但是 (f(d)) 不好求,你就可以通过莫比乌斯反演求得 (f(n))

    关于这道题

    我们首先设 (f_i) 为循环节长度恰好为 (i) 的字符串个数,那题目要的就是 (f_n),这点没有问题。

    那我们再弄一个 (g_i) 为循环节长度恰好为 (i) 的因子的字符串个数,那可以看出 (g_i=sumlimits_{d|i}f_d)
    那为什么要弄这个 (g_i) 呢?我们会想到,如果一个子串可以通过多次复制得到你这个字符串,那字符串长度就一定是这个子串的长度的倍数。那反过来,子串的长度一定是这个字符串长度的因数。
    那你会发现,任何长度为 (i) 的字符串的循环节长度都是 (i) 的因子的字符串个数。
    (g_i=26^i)

    那你会发现,你就可以反演了。
    (g_i=sumlimits_{d|i}f_dRightarrow f_i=sumlimits_{d|i}g_dmu_ frac{m}{d})

    代码

    #include<cstdio>
    #define ll long long
    #define mo 1000000007
    
    using namespace std;
    
    int n, yz[10001];
    ll ans;
    
    ll ksm(ll x, ll y) {//快速幂求 26^x
    	ll re = 1;
    	while (y) {
    		if (y & 1) re = (re * x) % mo;
    		x = (x * x) % mo;
    		y >>= 1;
    	}
    	return re;
    }
    
    void get_yz() {//求出这个数的所有因子
    	for (int i = 1; 1ll * i * i <= n; i++)
    		if (n % i == 0) {
    			yz[++yz[0]] = i;
    			if (i * i != n) yz[++yz[0]] = n / i;
    		}
    }
    
    ll get_miu(int x) {//求出一个数的 μ 值(定义法)
    	if (x == 1) return 1;
    	ll re = 1;
    	for (int i = 2; 1ll * i * i <= x; i++)
    		if (x % i == 0) {
    			re *= -1;
    			x /= i;
    			if (x % i == 0) return 0;
    		}
    	if (x > 1) re *= -1;
    	return re;
    }
    
    int main() {
    	scanf("%d", &n);
    	
    	get_yz();
    	
    	for (int i = 1; i <= yz[0]; i++)
    		ans = (ans + (ksm(26, yz[i]) * get_miu(n / yz[i]) % mo + mo) % mo) % mo;
    	
    	printf("%lld", ans);
    	
    	return 0;
    } 
    
    
  • 相关阅读:
    【focus-lei 】微服务
    queryURLParams
    时间字符串的处理
    str.charAt()与str[]的区别
    数组去重函数封装
    数组去重的几种方法
    splice与slice区别
    变量与属性名的区别
    parseInt parseFloat Number三者转换的方式
    原生js实现选项卡样式切换的几种方式。
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_8-7-2.html
Copyright © 2011-2022 走看看