OrzPandaCup B项链 (群论+杜教筛)

题目大意:

一串形状为n*2的环形项链,由1*2的小矩形组成,求1~n本质不同的所有方案所需要的矩形数。经过旋转后相同的视为同一种方案。

题解:

显然这是等价类计数问题,我们需要先计算不考虑循环同构前的方案数f(n)。

所以我们要计算f(n)的表达式。如果没有环,那么方案数显然是斐波那契数列Fib(n)。有了环之后会增加两种情况。一种是两个矩形横跨起点,于是剩余空间是Fib(n-2)。另一种是两行中矩形错位排列,这种情况只在n为偶数时才可行,固定为2种方案。所以得到$$f(n)=Fib(n)+Fib(n-2)+2[n\%2==0], f(1)=1$$

然后根据Burnside引理,答案就是$$\sum_{m=1}^n\sum_{d|m}f(d)\varphi(n/d)$$本来Burnside引理计算环时要除以n,但题目中问的是矩形数量,刚好抵消了

然后发现这个算式的形式和推导杜教筛的式子是一样的。数论分块后,斐波那契数论的前缀和容易计算,phi的前缀和套用杜教筛即可,复杂度\(O(n^{2/3})\)。

$$
f(n)=Fib(n)+Fib(n-2)+2[n\%2==0], f(1)=1\\
Ans=\sum_{m=1}^n\sum_{d|m}f(d)\varphi(n/d)\\
=\sum_{d=1}^nf(d)\sum_{i=1}^{\lfloor n/d\rfloor}\varphi(i)\\
=\sum_{d=1}^n f(d) \Phi(\lfloor n/d\rfloor)
$$

  1. #include <algorithm>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <map>
  5. #include <iostream>
  6. using namespace std;
  7. const int maxn = 2000010;
  8. typedef long long ll;
  9. ll T, pri[maxn], cur, mu[maxn], sum_mu[maxn], Fib[maxn];
  10. bool vis[maxn];
  11. map<ll, ll> mp_mu;
  12.  
  13. ll S_mu(ll x) {
  14.   if (x < maxn) return sum_mu[x];
  15.   if (mp_mu[x]) return mp_mu[x];
  16.   ll ret = 1ll;
  17.   for (ll i = 2, j; i <= x; i = j + 1) {
  18.     j = x / (x / i);
  19.     ret -= S_mu(x / i) * (j - i + 1);
  20.   }
  21.   return mp_mu[x] = ret;
  22. }
  23. ll S_phi(ll x) {
  24.   ll ret = 0ll;
  25.   for (ll i = 1, j; i <= x; i = j + 1) {
  26.     j = x / (x / i);
  27.     ret += (S_mu(j) - S_mu(i - 1)) * (x / i) * (x / i);
  28.   }
  29.   return ((ret - 1) >> 1) + 1;
  30. }
  31. int P;
  32. void init(){
  33.   mu[1] = 1;
  34.   for (int i = 2; i < maxn; i++) {
  35.     if (!vis[i]) {
  36.       pri[++cur] = i;
  37.       mu[i] = -1;
  38.     }
  39.     for (int j = 1; j <= cur && i * pri[j] < maxn; j++) {
  40.       vis[i * pri[j]] = true;
  41.       if (i % pri[j])
  42.         mu[i * pri[j]] = -mu[i];
  43.       else {
  44.         mu[i * pri[j]] = 0;
  45.         break;
  46.       }
  47.     }
  48.   }
  49.   for (int i = 1; i < maxn; i++) sum_mu[i] = sum_mu[i - 1] + mu[i];
  50.   Fib[0]=Fib[1]=1;
  51.   for (int i = 2; i < maxn; i++) Fib[i]=(Fib[i-1]+Fib[i-2])%P;
  52. }
  53. ll fib(ll n){
  54. 	if (n<maxn) return Fib[n];
  55.     ll x=0,y=1,t;
  56.     for (int d=62-__builtin_clzll(++n);d>=0;d--){
  57.         t=(x*x+y*y)%P; y=(x+x+y)*y%P; x=t;
  58.         if (n>>d&1) x=y,y=(t+y)%P;
  59.     }
  60.     return x;
  61. }
  62. ll S_F(ll x){
  63. 	if (x<2) return x;
  64. 	return (fib(x+2)+fib(x)+x-(x&1)-3)%P;
  65. }
  66.  
  67. int main(){
  68.  	ll n;
  69. 	scanf("%lld%lld",&n,&P);
  70. 	init();
  71. 	ll sum=0;
  72. 	for (ll i=1,j;i<=n;i=j+1){
  73. 		j=n/(n/i);
  74. 		//cout<<i<<' '<<S_F(j)-S_F(i-1)<<' '<<S_phi(n/i)<<'\n';
  75. 		sum+=(S_F(j)-S_F(i-1)+P)*S_phi(n/i)%P;
  76. 	}
  77. 	printf("%lld\n",sum%P);
  78.    	return 0;
  79. }

1 评论

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注