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;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步