奇怪的数论
不想写博客,但是感觉自己又不是很会,只能写上便于自己以后应用了
P2261余数求和 数论分块
数论分块的应用:求解关于\(\left\lfloor\dfrac{n}{i}\right\rfloor\)的求和问题。
对于任意一个\(i\)\((i\leqslant n)\),我们需要找到一个最大的\(j\)\((i\leqslant j\leqslant n)\)使得 \(\left\lfloor\frac{n}{i}\right\rfloor\) =\(\left\lfloor\frac{n}{j}\right\rfloor\),此时\(j\)=\(\left\lfloor\frac{n}{\left\lfloor\frac{n}{i}\right\rfloor}\right\rfloor\).
此时我们发现在一段区间内\(\left\lfloor\frac{n}{i}\right\rfloor\),的值是相同的,所以我们用这一段的长度\(\times\)这一段的值,分块求解。
int ans=0;
for(int l = 1,r = 0;l <= n;l=r+1){
r=n/(n/l); // 求区间的右端,这是一个数学规律
ans+=(r-l+1)*(n/l);
}
那么对于这道题而言\(\sum\limits_{i=1}^n{kmodi}\),它等价于\(\sum\limits_{i=1}^n {k-\left\lfloor\dfrac{k}{i}\right\rfloor \times i}\),再变形一下就变成了\(nk-\sum\limits_{i=1}^n {\left\lfloor\dfrac{k}{i}\right\rfloor \times i}\),这样求余数和的问题就被我们转化乘求\(\sum\limits_{i=1}^n {\left\lfloor\dfrac{k}{i}\right\rfloor} \times i\)的问题
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
long long n,k;
long long solve(long long n,long long k){
long long ans=n*k;
for (long long l=1,r;l <= n;l=r+1){
if (k/l!=0) r=min(k/(k/l),n);
else r=n;
ans-=(k/l)*(r-l+1)*(l+r)/2;//区间长度*区间平均值*向下取整的贡献
}
return ans;
}
int main(){
scanf ("%lld%lld",&n,&k);
long long ans=solve(n,k);
printf("%lld\n",ans);
return 0;
}
P3708 koishi的数学题 线性筛求约数和
虽然和第一题长的比较像,但是好像没什么关系,当然还是需要变化式子:
\(\sum\limits_{k=1}^n {\sum\limits_{i=1}^n {xmodi}}\),先看里面的公式,和上一道题完全一样的变形\(nx-\sum\limits_{i=1}^n {\left\lfloor\dfrac{x}{i}\right\rfloor \times i}\),再看外层嵌套的公式,如果我们能推出相邻两个之间的关系,那么我们就可以递推出整个式子.
\(\begin{aligned}f(x) & = n \times x-\sum\limits_{i=1}^n {\left\lfloor\dfrac{x}{i}\right\rfloor \times i }\end{aligned}\)
\(\begin{aligned}f(x+1) & = n \times (x+1) - \sum\limits_{i=1}^n {\left\lfloor\dfrac{x
+1}{i}\right\rfloor \times i}\end{aligned}\)
两式相减得到:\(\begin{aligned}f(x+1)-f(x) & = n-\sum\limits_{i=1}^n {\left(\left\lfloor\dfrac{x+1}{i}\right\rfloor-\left\lfloor\dfrac{x}{i}\right\rfloor\right)}\end{aligned}\)
对于最后一个式子,明显只有当\(i|(x+1)\)的时候,\(\left\lfloor\dfrac{x+1}{i}\right\rfloor-\left\lfloor\dfrac{x}{i}\right\rfloor = 1\),否则为0
因此只有\(i\)是\(x+1\)的因数时,才会对答案有贡献,所以对于做外层的\(k\)来说,答案就是\(k\)的因数和,线性筛求因数和这东西就……先背下来吧。
#include <iostream>
#include <cstdio>
#include <algorithm>
#define int long long
using namespace std;
const int maxn=1e6+10;
int n,tot;
int g[maxn],f[maxn],primelist[maxn],dp[maxn],vis[maxn];
void init(){
g[1]=f[1]=1;
for (int i = 2;i <= n;i++){
if (!vis[i]) vis[i]=1,primelist[++tot]=i,g[i]=f[i]=i+1;
for (int j = 1;j <= tot&&i*primelist[j]<=n;j++){
vis[primelist[j]*i]=1;
if (i%primelist[j]==0){
g[i*primelist[j]]=g[i]*primelist[j]+1;
f[i*primelist[j]]=f[i]/g[i]*g[i*primelist[j]];
break;
}
else{
f[i*primelist[j]]=f[i]*f[primelist[j]];
g[i*primelist[j]]=1+primelist[j];
}
}
}
// for (int i = 1;i <= n;i++) f[i]=f[i-1]+f[i];
}
signed main(){
scanf ("%lld",&n);
init();
for (int i = 1;i <= n;i++) dp[i]=dp[i-1]+n-f[i];
for (int i = 1;i <= n;i++) printf("%lld ",dp[i]);
return 0;
}
P1544 蜈蚣 前缀异或和
dp外加前缀异或和,和前缀和差不多,记一下就好了
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a[1000005],ans[1000005];
int n,m;
int main(){
cin>>n;
for(int i = 1; i<=n; i++) {
scanf("%lld",&a[i]);
ans[i] = ans[i-1] ^ a[i];
}
int l,r;
while(m--) {
scanf("%d%d",&l,&r);
printf("%lld\n",ans[r] ^ ans[l - 1]);
}
return 0;
}