NOI2016 循环之美

NOI2016 循环之美

复习莫比乌斯反演...

  • 需要/可能用到的知识点:

\[[i\bot j]=\sum_{d|gcd(i,j)} \mu(d)=\sum_{d|i,d|j}\mu(d) \]

\[\forall i\bot j,\mu(ij)=\mu(i)\mu(j) \]

\[\forall i\not \bot j,\mu(ij)=0 \]

  • 杜教筛相关

    \[g(1) S(n)=\sum_{i=1}^{n}(f * g)(i)-\sum_{i=2}^{n} g(i) S\left(\left\lfloor\frac{n}{i}\right\rfloor\right) \]

    ll GetSum(int n) { // 算 f 前缀和的函数
      ll ans = f_g_sum(n); // 算 f * g 的前缀和
      // 以下这个 for 循环是数论分块
      for(ll l = 2, r; l <= n; l = r + 1) { // 注意从 2 开始
        r = (n / (n / l)); 
        ans -= (g_sum(r) - g_sum(l - 1)) * GetSum(n / l);
        // g_sum 是 g 的前缀和
        // 递归 GetSum 求解
      } return ans; 
    }
    

    然后我们先筛出前\(n^\frac 2 3\)个答案,总复杂度\(n^{\frac 2 3}\)

然后做推式子题的时候注意函数本身的性质、计算顺序、能否递归等

这题推出来的式子是

\[\sum_{d=1}^n[d\bot k]\mu(d)\left\lfloor\frac n d\right\rfloor \sum_{j=1}^{m/d}[j\bot k] \]

然后要算这两个的前缀和

\[f(n)=\sum_{i=1}^n[i\bot k]\\S(n)=\sum_{i=1}^n[i\bot k]\mu(i) \]

相当于被分成了k段,这k段的f都是相同的,然后单独考虑最后一段

所以

\[f(n)=\left\lfloor\frac n k\right\rfloor f(k)+f(x\text{%} k) \]

预处理出k以内的值就好了

对于第二个,

\[S(n,k)=\sum_{i=1}^n [i\bot k]|\mu(i)\\=\sum_{i=1}^n \mu(i)\sum_{d|i,d|k}\mu(d) \]

\[=\sum_{d|k}\mu(d)\sum_{d|i}\mu(i) \]

\[=\sum_{d|k}\mu(d)\sum_{i=1}^{x/d}\mu(id) \]

\[=\sum_{d|k}\mu(d)\sum_{i=1,i\bot d}^{x/d}\mu(i)\mu(d) \]

\[=\sum_{d|k}\mu^2(d)\sum_{i=1}^{x/d}[i\bot d]\mu(i) \]

\[=\sum_{d|k}\mu^2(d)S(\left\lfloor\frac x d\right\rfloor,d) \]

边界:\(g(n,1)=\sum_{i=1}^n \mu(i)\),杜教筛即可。

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
using namespace std;
int read(){
	int x=0,pos=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
	return pos?x:-x;
}
const int N =2e6;
int n,m,k;
int np[N],mu[N],f[N],pri[N],tot,sm[N];
int gcd(int a,int b){
	return b?gcd(b,a%b):a;
}
void init(){
	mu[1]=1;
	FOR(i,1,2000) f[i]=f[i-1]+(gcd(i,k)==1);
	FOR(i,2,N-1){
		if(!np[i]) pri[++tot]=i,mu[i]=-1;
		for(int j=1;j<=tot&&pri[j]*i<N;j++){
			int now=i*pri[j];np[now]=1;
			if(i%pri[j]==0){
				mu[now]=0;break; 
			}else mu[now]=mu[i]*-1;
		}
	}
	FOR(i,1,N-1) sm[i]=sm[i-1]+mu[i];
}
ll F(int now){
	return now/k*f[k]+f[now%k]; 
}
#define pbl pair<bool,ll>
#define mp make_pair
struct node{
	int key1,key2;ll res;int nex;
}; 
pbl nq;
struct hash_table{
	int head[20000001];node edge[20000001];
	int top=0;
	int hash(int k1,int k2){
		return (1ll*abs(k1)*9982443537+abs(k2))%20000000; 
	} 
	void add(int u,int k1,int k2,ll w){
		edge[++top].res=w;
		edge[top].key1=k1;
		edge[top].key2=k2;
		edge[top].nex=head[u];
		head[u]=top;
	}
	void insert(int k1,int k2,ll w){
		int u=hash(k1,k2);
		add(u,k1,k2,w);
	}
	void count(int k1,int k2){
		int u=hash(k1,k2);
		for(int i=head[u];i;i=edge[i].nex){
			if(edge[i].key1==k1&&edge[i].key2==k2) return nq=mp(1,edge[i].res),void();
		}
		nq=mp(0,0);return;
	}
}M;
ll S(int now,int k){
	if(!now||(k==1&&now<N)) return sm[now];
	M.count(now,k);
	if(nq.first) return nq.second;
	ll res=0;
	if(k==1){
		res=1;
		for(ll l=2,r;l<=now;l=r+1){
			r=now/(now/l);
			res-=(r-l+1)*(S(now/l,1));
		}
	}else{
		for(int i=1;i*i<=k;i++){
			if(k%i) continue;
			if(mu[i]) res+=S(now/i,i);
			if(mu[k/i]&&i!=k/i) res+=S(now/(k/i),k/i);
		}
	}
	return M.insert(now,k,res),res;
}
int main(){
	n=read(),m=read(),k=read();
	init();int pre=0;int r;
	ll ans=0;
	for(int l=1;l<=min(n,m);l=r+1){
		r=min((n/(n/l)),(m/(m/l)));
		int now=S(r,k);
		ans+=1ll*(now-pre)*(n/l)*F(m/l);
		pre=now;
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2020-05-19 11:41  lcyfrog  阅读(182)  评论(0编辑  收藏  举报