不想写博客,但是感觉自己又不是很会,只能写上便于自己以后应用了
P2261余数求和 数论分块
数论分块的应用:求解关于(leftlfloordfrac{n}{i} ight floor)的求和问题。
对于任意一个(i)((ileqslant n)),我们需要找到一个最大的(j)((ileqslant jleqslant n))使得 (leftlfloorfrac{n}{i}
ight
floor) =(leftlfloorfrac{n}{j}
ight
floor),此时(j)=(leftlfloorfrac{n}{leftlfloorfrac{n}{i}
ight
floor}
ight
floor).
此时我们发现在一段区间内(leftlfloorfrac{n}{i}
ight
floor),的值是相同的,所以我们用这一段的长度( imes)这一段的值,分块求解。
int ans=0;
for(int l = 1,r = 0;l <= n;l=r+1){
r=n/(n/l); // 求区间的右端,这是一个数学规律
ans+=(r-l+1)*(n/l);
}
那么对于这道题而言(sumlimits_{i=1}^n{kmodi}),它等价于(sumlimits_{i=1}^n {k-leftlfloordfrac{k}{i} ight floor imes i}),再变形一下就变成了(nk-sumlimits_{i=1}^n {leftlfloordfrac{k}{i} ight floor imes i}),这样求余数和的问题就被我们转化乘求(sumlimits_{i=1}^n {leftlfloordfrac{k}{i} ight floor} imes 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
",ans);
return 0;
}
P3708 koishi的数学题 线性筛求约数和
虽然和第一题长的比较像,但是好像没什么关系,当然还是需要变化式子:
(sumlimits_{k=1}^n {sumlimits_{i=1}^n {xmodi}}),先看里面的公式,和上一道题完全一样的变形(nx-sumlimits_{i=1}^n {leftlfloordfrac{x}{i}
ight
floor imes i}),再看外层嵌套的公式,如果我们能推出相邻两个之间的关系,那么我们就可以递推出整个式子.
(egin{aligned}f(x) & = n imes x-sumlimits_{i=1}^n {leftlfloordfrac{x}{i}
ight
floor imes i }end{aligned})
(egin{aligned}f(x+1) & = n imes (x+1) - sumlimits_{i=1}^n {leftlfloordfrac{x
+1}{i}
ight
floor imes i}end{aligned})
两式相减得到:(egin{aligned}f(x+1)-f(x) & = n-sumlimits_{i=1}^n {left(leftlfloordfrac{x+1}{i}
ight
floor-leftlfloordfrac{x}{i}
ight
floor
ight)}end{aligned})
对于最后一个式子,明显只有当(i|(x+1))的时候,(leftlfloordfrac{x+1}{i}
ight
floor-leftlfloordfrac{x}{i}
ight
floor = 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
",ans[r] ^ ans[l - 1]);
}
return 0;
}