1. 程式人生 > >【NOI2016】迴圈之美,mobius反演+杜教篩

【NOI2016】迴圈之美,mobius反演+杜教篩

思路:
對於 xy ,我們肯定讓 gcd(x,y)=1 ,因為這樣計算不會重複,而且比不互質時更優,且只要餘數出現重複就是有迴圈節了,根據十進位制小數轉k進位制的方法,我們可以得到
xks=x(mody)ks=1(mody) y|x
因為 gcd(x,y)=1 ,所以 gcd(ks,y)=1

,也就是 gcd(k,y)=1
我們要求的式子就是 i=1nj=1m[gcd(i,j)=1][gcd(j,k)=1]
[gcd(i,j)=1] 移到後面,反演一下,再在前面更換指標
dμ(d)ndj=1md[gcd(dj,k)=1]

(然後我就反演了半天,最後被reflash和mrazer一眼秒了)
因為 gcd(dj,k)=1gcd(d,k)=1,gcd(j,k)=1 ,所以
gcd(d,k)=1μ(d)ndF(md) ,其中 F(m)=i=1m[gcd(i
,k)=1]

關於F函式,因為 gcd(i,k)=gcd(i+k,k) ,所以很多都是重複的,可以預處理 k 以內的F,然後就能 O(1) 算了
(我原本沒預處理,暴力算的,在uoj跑76分,問了reflash才知道可以 O(1) 算的)
前面部分根據d的取值分塊算,現在問題就在於如何快速計算 gcd(d,k)=1μ(d)
%一發reflash
(這個我真沒想到,卡在這裡挺久的,因為沒有把 μ(di) 拆開看)
g(n)=i=1n[gcd(i,k)=1]μ(i),M(n)=i=1nμ(i)
g(n)=M(n)d=2ni=1n[gcd(i,k)=d]μ(i)=M(n)d=2,d|kni=1nd[gcd(i,kd)=1]μ(di)=M(n)d=2,d|kni=1nd[gcd(i,kd)=1]μ(d)μ(i)[gcd(i,d)=1]
兩個互質條件合併一下,就是 [gcd(i,k)=1]
g(n)=M(n)d=2,d|knμ(d)g(nd)
非常像杜教篩的處理方法,記憶化即可
小範圍的時候可以暴力
程式碼:

#include<cstdio>
#include<iostream>
#define LL long long
#define ui unsigned int
using namespace std;
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int n,m,k;
const int lim=2000000;
int prime[lim/10+5],f[2005],py[2005];
ui pr[2005];
short mu[lim+5],sum[lim+5];
char vis[lim+5];
void init()
{
    mu[1]=1;
    int