2020,1月 第1周练习总结

第一题 洛谷 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=1ki 
  = ∑ i = 1 n k − k i ∗ i   \sum_{i=1}^n k -\frac{k}{i}*i  i=1kiki 
  =nk -  ∑ i = 1 n k i ∗ i   \sum_{i=1}^n\frac{k}{i}*i  i=1iki 
很显然这道题是用分块来做的

#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=1nj=1m(ni)(mj)
   其中  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=1nj=1m(ni)(mj)
     = ∑ 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=1nj=1m(nini)(mjmj)
     
   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(nn/ii)(mm/ii)
  = ∑ 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(nm+inimi2i(min+nim))
  其中 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=1nj=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=1ni=1nj=1m[gcd(i,j)=k](kprime)
   = ∑ 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=1ni=1n/kj=1m/k[gcd(i,j)=1](kprime)
   接下来就是莫比乌斯反演
   由公式 [ gcd(i,j) = 1 ] = ∑ d ∣ g c d ( i , j ) μ ( d ) \sum_{d|gcd(i,j)} μ(d) dgcd(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=1ni=1n/kj=1m/kdgcd(i,j)μ(d)(kprime)
   = ∑ 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=1nd=1n/kkdnkdmμ(d)(kprime)
   令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=1nd=1n/kTnTmμ(d)(kprime)
   再将 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=1nTnTmkT,(kprime)μ(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值