第一题 洛谷 P 2261
题意: 输入n,k,求 k mod 1+k mod 2+k mod 3+⋯+k mod n;
题解:
∑
i
=
1
n
k
%
i
\sum_{i=1}^n k%i
∑i=1nk%i
=
∑
i
=
1
n
k
−
k
i
∗
i
\sum_{i=1}^n k -\frac{k}{i}*i
∑i=1nk−ik∗i
=nk -
∑
i
=
1
n
k
i
∗
i
\sum_{i=1}^n\frac{k}{i}*i
∑i=1nik∗i
很显然这道题是用分块来做的
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main()
{
LL n,k;
while(scanf("%lld%lld",&n,&k) !=EOF)
{
LL ans = n*k;
for(LL l=1,r,t;l<=n;l=r+1)
{
t = k/l;
t == 0? r=n:r=min(k/t,n);
ans -= t*(r-l+1)*(r+l)/2;
}
printf("%lld\n",ans);
}
return 0;
}
第二题 洛谷P2260
题意 :
∑
i
=
1
n
∑
j
=
1
m
(
n
%
i
)
*
(
m
%
j
)
\sum_{i=1}^n\sum_{j=1}^m ( n%i )*(m%j)
i=1∑nj=1∑m(n%i)*(m%j)
其中 i != j
题解:
这道题和上面的那道题很想只是把难度提高了而已,思路是不变的
我们可以先不管 i != j,且令 n < m;
令 含有 i != j 的和为 ans1,只含有 i != j 的和为 ans2;
ans1 = :
∑
i
=
1
n
∑
j
=
1
m
(
n
%
i
)
*
(
m
%
j
)
\sum_{i=1}^n\sum_{j=1}^m ( n%i )*(m%j)
∑i=1n∑j=1m(n%i)*(m%j)
=
∑
i
=
1
n
∑
j
=
1
m
(
n
−
n
i
∗
i
)
*
(
m
−
m
j
∗
j
)
\sum_{i=1}^n\sum_{j=1}^m ( n -\frac{n}{i}*i )*(m-\frac{m}{j}*j)
∑i=1n∑j=1m(n−in∗i)*(m−jm∗j)
ans2 =
∑
i
=
1
n
(
n
−
n
/
i
∗
i
)
*
(
m
−
m
/
i
∗
i
)
\sum_{i=1}^n ( n - n/i*i )*(m-m/i*i)
∑i=1n(n−n/i∗i)*(m−m/i∗i)
=
∑
i
=
1
n
(
n
∗
m
+
n
i
∗
m
i
∗
i
2
−
i
∗
(
m
∗
n
i
+
n
∗
m
i
)
)
\sum_{i=1}^n ( n*m + \frac{n}{i}*\frac{m}{i}*i^2 - i*( m*\frac{n}{i}+n*\frac{m}{i}) )
∑i=1n(n∗m+in∗im∗i2−i∗(m∗in+n∗im))
其中 ans1 可以用两个分块相乘即可求出答案
ans2 可以分步骤求出他的和,
最后用 ans1-ans2就可求出答案;
由于有
i
2
i^2
i2 和 i 的前n项和,所以可以提前把2和6的逆元求出
具体的一些细节我会在代码中加入备注
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 19940417;
const int inv2 = 9970209;
const int inv6 = 3323403;
LL fun(LL l,LL r) //求i的前n项和
{
return (r-l+1)*(r+l)/2%mod;
}
LL calc(LL x) // 整除分块
{
LL ans=x*x%mod;
for(LL l=1,r;l<=x;l=r+1)
{
r = x/(x/l);
ans = (ans-(x/l)*fun(l,r)+mod)%mod;
}
return ans;
}
LL sum(LL x) //求 i^2 的前 n 项和
{
return x*(x+1)%mod*(2*x+1)%mod*inv6%mod;
}
int main()
{
LL n,m;
scanf("%lld%lld",&n,&m);
LL ans = calc(n)*calc(m)%mod;
if( n > m )
swap(n,m);
ans = (ans-m*n%mod*n%mod+mod)%mod;
for(LL l=1,r;l<=n;l=r+1)// 利用整除分块原理将ans2的各部分进行分步处理
{
r = min(n/(n/l),m/(m/l));
LL ans1 = (n/l)*(m/l)%mod*(sum(r)-sum(l-1)+mod)%mod;
LL ans2 = (n/l*m%mod+m/l*n%mod)%mod*fun(l,r)%mod;
ans = (ans-(ans1-ans2+mod)%mod+mod)%mod;
}
printf("%lld\n",ans);
return 0;
}
第三题 P3802
题意: 输入a,b,c,d,e,f,g,7个数,每个代表字母的个数,然后将他们全排序,如果存在(abcdefg)在
一起,(中间顺序没有影响),则会成功,现在求成功的期望是多少
题解: 我们可以任选其中的字母,顺序不影响成功,令 n = a+b+c+d+e+f+g;
ans = 7! * a/n * b/(n-1) * c/(n-2) * d/(n-3) * e/(n-4) * f/(n-5) * g/(n-6) * (n-6)
这里最后乘的(n-6)相当于将一组成功的样例捆绑在一起在去插剩下的空;
#include<bits/stdc++.h>
using namespace std;
double a[10];
int main()
{
int n;
for(int i=1;i<=7;i++)
{
scanf("%lf",&a[i]);
n += a[i];
}
double ans=5040;
for(int i=1;i<=6;i++)
ans *= a[i]/(n-double(i-1));
ans *= a[7];
printf("%.3f\n",ans);
return 0;
}
第四题 codeforces 1208G http://codeforces.com/problemset/problem/1208/G
题意: 在边数[3,n]的正多边形中找出K个正多边形,将他们共圆,使得圆上出现点的个数最少;
题解: 我们可以先固定一个点为原点,显然,如果我们先构成了正x边形,那么就应该已经构成了
正 x 的因数 的多边形,由此可以推出当我们已经构成的 x 因数多边型,那么只要在加上
phi(x) 个点 就可以构成正x边形,因为我们先固定了原点,所以我们只需要加点就可以构
成新的正多边形;
#include<stdio.h>
#include<algorithm>
using namespace std;
#define re register
typedef long long LL;
const int N = 1e6+10;
int prime[N],phi[N];
bool vis[N];
int a[N];
int main()
{
int n,k;
scanf("%d%d",&n,&k);
if( k == 1 )//显然 k = 1 时 需要特判
{
printf("3\n");
return 0;
}
int cnt = 0;
for(int i=2;i<=n;i++)
{
if( vis[i] == 0 )
{
prime[++cnt] = i;
phi[i] = i-1;
}
for(int j=1;j<=cnt&&prime[j]*i<=n;j++)
{
vis[prime[j]*i] = 1;
if( i % prime[j] == 0 )
{
phi[i*prime[j]] = phi[i] * prime[j];
break;
}
phi[i*prime[j]] = phi[i] * phi[prime[j]];
}
}
cnt = 0;
for(int i=3;i<=n;i++)
a[cnt++] = phi[i];
sort(a,a+cnt);
LL ans = 2;
cnt = 0;
for(int i=0;i<k;i++)
ans += (LL)a[i];
printf("%lld\n",ans);
return 0;
}
第五题 洛谷P2257
题意: 给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对
题解:
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
∈
p
r
i
m
e
]
\sum_{i=1}^n\sum_{j=1}^m [gcd(i,j)∈ prime ]
∑i=1n∑j=1m[gcd(i,j)∈prime]
我们不妨枚举k
∑
k
=
1
n
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
k
]
(
k
∈
p
r
i
m
e
)
\sum_{k=1}^n\sum_{i=1}^n\sum_{j=1}^m [gcd(i,j)=k ] (k∈prime)
∑k=1n∑i=1n∑j=1m[gcd(i,j)=k](k∈prime)
=
∑
k
=
1
n
∑
i
=
1
n
/
k
∑
j
=
1
m
/
k
[
g
c
d
(
i
,
j
)
=
1
]
(
k
∈
p
r
i
m
e
)
\sum_{k=1}^n\sum_{i=1}^{n/k}\sum_{j=1}^{m/k} [gcd(i,j)=1 ] (k∈prime)
∑k=1n∑i=1n/k∑j=1m/k[gcd(i,j)=1](k∈prime)
接下来就是莫比乌斯反演
由公式 [ gcd(i,j) = 1 ] =
∑
d
∣
g
c
d
(
i
,
j
)
μ
(
d
)
\sum_{d|gcd(i,j)} μ(d)
∑d∣gcd(i,j)μ(d) 得:
∑
k
=
1
n
∑
i
=
1
n
/
k
∑
j
=
1
m
/
k
∑
d
∣
g
c
d
(
i
,
j
)
μ
(
d
)
(
k
∈
p
r
i
m
e
)
\sum_{k=1}^n\sum_{i=1}^{n/k}\sum_{j=1}^{m/k}\sum_{d|gcd(i,j)} μ(d) (k∈prime)
∑k=1n∑i=1n/k∑j=1m/k∑d∣gcd(i,j)μ(d)(k∈prime)
=
∑
k
=
1
n
∑
d
=
1
n
/
k
n
k
d
∗
m
k
d
∗
μ
(
d
)
(
k
∈
p
r
i
m
e
)
\sum_{k=1}^n\sum_{d=1}^{n/k}\frac{n}{kd}* \frac{m}{kd}* μ(d) (k∈prime)
∑k=1n∑d=1n/kkdn∗kdm∗μ(d)(k∈prime)
令T = kd
=
∑
k
=
1
n
∑
d
=
1
n
/
k
n
T
∗
m
T
μ
(
d
)
(
k
∈
p
r
i
m
e
)
\sum_{k=1}^n\sum_{d=1}^{n/k} \frac{n}{T}* \frac{m}{T} μ(d) (k∈prime)
∑k=1n∑d=1n/kTn∗Tmμ(d)(k∈prime)
再将 T 提出来
=
∑
T
=
1
n
n
T
∗
m
T
∑
k
∣
T
,
(
k
∈
p
r
i
m
e
)
μ
(
T
/
k
)
\sum_{T=1}^n \frac{n}{T}* \frac{m}{T} \sum_{k|T,(k∈prime)} μ(T/k)
∑T=1nTn∗Tm∑k∣T,(k∈prime)μ(T/k)
我们现在只用考虑每一个质数kk,对于k的倍数T,将它的值加上μ(
T
k
\frac{T}{k}
kT);
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7+10;
const int lim = 1e7;
int sum[N],prime[N],mu[N];
bool vis[N];
void init()
{ mu[1] = 1;
int cnt = 0;
for(int i=2;i<=lim;i++)
{
if( vis[i] == 0 )
{
prime[++cnt] = i;
mu[i] = -1;
}
for(int j=1; j<=cnt && prime[j]*i<=lim; j++ )
{
vis[i*prime[j]] = 1;
if( i%prime[j] == 0 )
break;
else
mu[i*prime[j]] = -mu[i];
}
}
for(int i=1;i<=cnt;i++)
for(int j=1;j*prime[i]<=lim; j++ )
sum[j*prime[i]] += mu[j];
for(int i=1;i<=lim;i++)
sum[i] += sum[i-1];
}
int main()
{
init();
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
if( n > m )
swap(n,m);
LL ans = 0;
for(int l=1,r; l<=n;l=r+1)
{
r = min(n/(n/l),m/(m/l));
ans += (LL)(n/l)*(LL)(m/l)*(LL)(sum[r]-sum[l-1]);
}
printf("%lld\n",ans);
}
return 0;
}