bzoj 2190 //2190: [SDOI2008]仪仗队 欧拉函数/容斥原理/线性筛+欧拉函数/莫比乌斯反演/莫比乌斯反演+分块+前缀和

bzoj 2190 //2190: [SDOI2008]仪仗队   //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=2190
//在线测评地址https://www.luogu.com.cn/problem/P2158

更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录

关于数学公式,信奥讲究的是会用,至于证明,显然要高好几个层次,若学有余力,可适当钻研。2019-12-13

方法一:欧拉函数

Accepted 824 kb 140 ms C++/Edit 531 B

//2190: [SDOI2008]仪仗队
//在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=2190
//在线测评地址https://www.luogu.com.cn/problem/P2158
//通过画图,很快弄懂题意。
//本质是以左下角为坐标原点,与队列中的学生进行连线,有多少种不同的直线。即为能看到的学生人数。
//1 ≤ N ≤ 40000,枚举算法超时无疑。
//第一想法,找出GCD(x,y)=1的个数即可,加上横坐标,纵坐标,2种情况即可。
//1<=x<=N,1<=y<=N
//感觉有莫比乌斯反演的痕迹在里面。该题还有可能一题多解。
//又想到样例N=4
//y=1,x<=1与y=1有互质的数量有1;
//y=2,x<=2与y=2有互质的数量有1;
//y=3,x<=3与y=3有互质的数量有2;
//+横坐标,纵坐标,2种情况
//数量为1+1*2+2*2+2=9
//这不是求欧拉函数吗。
//若不明白,详见下图的推导。2019-12-8 17:01

bzoj 2190 //2190: [SDOI2008]仪仗队 欧拉函数/容斥原理/线性筛+欧拉函数/莫比乌斯反演/莫比乌斯反演+分块+前缀和_第1张图片

bzoj 2190 //2190: [SDOI2008]仪仗队 欧拉函数/容斥原理/线性筛+欧拉函数/莫比乌斯反演/莫比乌斯反演+分块+前缀和_第2张图片

bzoj 2190 //2190: [SDOI2008]仪仗队 欧拉函数/容斥原理/线性筛+欧拉函数/莫比乌斯反演/莫比乌斯反演+分块+前缀和_第3张图片

 如图,n=4,能被看到的点(1,0),(0,1),(1,1)有3个;(1,2),(2,1) 1*2=2个 即2*phi(2)=2;(1,3),(2,3),(3,2),(3,1) 2*2=4个 即2*phi(3)=4个;

总计3+1*2+2*2=9个。2019-12-8 18:46   3+phi(2)*2+phi(3)*2=3+1*2+2*2=9

bzoj 2190 //2190: [SDOI2008]仪仗队 欧拉函数/容斥原理/线性筛+欧拉函数/莫比乌斯反演/莫比乌斯反演+分块+前缀和_第4张图片

n=5,3+phi(2)*2+phi(3)*2+phi(4)*2=3+1*2+2*2+2*2=13


//回顾了此文https://blog.csdn.net/mrcrack/article/details/102770004重新学习了欧拉函数的写法
//测试了n=40000的情况,秒出。
//样例通过,提交AC.2019-12-8 19:44
//欧拉函数还不熟练,还需加强。规律找得不错。

#include 
int prime[300],cnt;
int phi(int x){
	int ret=x,i;
	cnt=0;//漏了此句
	for(i=2;i*i<=x;i++){
		if(x%i==0){
			prime[++cnt]=i;
			while(x%i==0)x/=i;
		}
	}
	if(x>1)prime[++cnt]=x;
	for(i=1;i<=cnt;i++)ret/=prime[i],ret*=(prime[i]-1);
	return ret;
}
int main(){
	int n,i,ans=3;
	scanf("%d",&n);
	if(n==1)printf("1\n");
	else if(n==2)printf("3\n");
	else{
		for(i=2;i<=n-1;i++)ans+=phi(i)*2;
		printf("%d\n",ans);
	}
	return 0;
}

关于欧拉函数公式的得出,此文https://www.cnblogs.com/Parsnip/p/10685470.html介绍得不错,摘抄如下

bzoj 2190 //2190: [SDOI2008]仪仗队 欧拉函数/容斥原理/线性筛+欧拉函数/莫比乌斯反演/莫比乌斯反演+分块+前缀和_第5张图片

对于求解1−n所有数的欧拉函数,如果直接使用分解质因数法,时间复杂度为O(n*sqrt(n))。

方法二:容斥原理

Accepted 976 kb 28 ms C++/Edit 572 B

 //此文https://www.luogu.com.cn/problemnew/solution/P2158   作者: Regimes 更新时间: 2019-01-06 16:54思路不错,摘抄如下

bzoj 2190 //2190: [SDOI2008]仪仗队 欧拉函数/容斥原理/线性筛+欧拉函数/莫比乌斯反演/莫比乌斯反演+分块+前缀和_第6张图片

/*
去重部分解释如下:
n=11,因暂不考虑落在横坐标,纵坐标上的点,n--,n=10
能被2整除的数有:2,4,6,8,10    需扣除4,6,8,10    等差数列,公差为2
能被3整除的数有:3,6,9    需扣除6,9    等差数列,公差为3
能被4整除的数有:4,8    需扣除8
能被5整除的数有:5,10    需扣除10
能被6整除的数有:6        唯一
能被7整除的数有:7        唯一
能被8整除的数有:8        唯一
能被9整除的数有:9        唯一
能被10整除的数有:10      唯一

故代码需逆序编写
    for(i=n;i>=2;i--){//逆序处理,详见n=11的讨论
        f[i]=(n/i)*(n/i);
        for(j=i+i;j<=n;j+=i)f[i]-=f[j];
        ans-=f[i];
    }
*/

//样例通过,提交AC.2019-12-9

#include 
int f[40010];//此处错写成int f[20010];造成了 Runtime_Error	900 kb	0 ms	C++/Edit	492 B
int main(){
	int n,ans,i,j;
	scanf("%d",&n);
	if(n==1){printf("0\n");return 0;}//特判,别忘了。
	ans=(n-1)*(n-1),n--;//落在横坐标,纵坐标上的点另行讨论
	for(i=n;i>=2;i--){//逆序处理,详见n=11的讨论
		f[i]=(n/i)*(n/i);
		for(j=i+i;j<=n;j+=i)f[i]-=f[j];
		ans-=f[i];
	}
	printf("%d\n",ans+2);//加上(1,0),(0,1)这两个落在横坐标,纵坐标上看不见的点
	return 0;
}

方法三:线性筛+欧拉函数

Accepted 1152 kb 32 ms C++/Edit 647 B

//对线性筛的理解,还是举几个例子,印象更深
/*
phi[1]=1,phi[2]=1,phi[3]=2,phi[4]=2,phi[5]=4,phi[6]=2,phi[7]=6,phi[8]=4,phi[9]=6,phi[10]=4
1. 特判phi[i]=1
2. 质数系列phi[2]=1,phi[3]=2,phi[5]=4,phi[7]=6  可见phi[p]=p-1,若p是质数。
3. 2%2==0,phi[2*2]=phi[2]*2=1*2=2;4%2==0,phi[4*2]=phi[4]*2=2*2=4;3%3==0,phi[3*3]=phi[3]*3=2*3=6;  i%p==0,phi[i*p]==phi[i]*p;
4. 3%2!=0,phi[3*2]=phi[3]*(2-1)=2;
*/
//该题,具体思路,可见   方法一
//样例通过,提交AC.2019-12-11

以下https://blog.csdn.net/summonlight/article/details/51967425摘自

以下全文转载自 http://www.cnblogs.com/372465774y/archive/2012/10/16/2726282.html

Update:本人根据评论进行了一些小修改。

函数的积性即:若m,n互质,则φ(mn)=φ(m)φ(n)。

//  下面是百度上找的错误证明
由“m,n互质”可知m,n无公因数,所以φ(m)φ(n)=m(1-1/p1)(1-1/p2)
(1-1/p3)…(1-1/pn)·n(1-1/p1')(1-1/p2')(1-1/p3')…(1-1/pn'),
//这里已经用了计算公式,而计算公式是需要先有积性前提才推导的

其中p1,p2,p3...pn为m的质因数,p1',p2',p3'...pn'为n的质因数,而m,n无公因数,
所以p1,p2,p3...pn,p1',p2',p3'...pn'互不相同,
所以p1,p2,p3...pn,p1',p2',p3'...pn'均为mn的质因数且为mn质因数的全集,
所以φ(mn)=mn(1-1/p1)(1-1/p2)(1-1/p3)…(1-1/pn)
(1-1/p1')(1-1/p2')(1-1/p3')…(1-1/pn'),
所以φ(mn)=φ(m)φ(n)。

查了很多资料会证明了: 
// 证明:
  在证明前先了解下以下知识:
  (a,b)代表最大公约数,[a,b]代表最小公倍数
  m|(a-b) <=> a≡b (mod m)
  a=pm+r  (0<=rm|(ac-bd)
补充证明:a=b+km,c=d+lm,a*c=(b+km)*(d+lm),可得  (ac-bd)=klm^2+(b+d)m =>m|(ac-bd)
  性质6:若a≡b(mod m),那么an≡bn(mod m),(其中n为自然数)。
   证明 : m|(a-b) => m|n*(a-b) 
  性质7:若ac≡bc(mod m),(c,m)=1,那么a≡b(mod m),(记号(c,m)表示c与m的最大公约数)。
   证明 : m|c(a-b) d=(m,c)=>m/d|(a-b) => a≡b(mod m/d)=>当 d=1时 即(c,m)=1上面结论成立
  性质8:若a≡b(mod m),那么a的n次方和b的n次方也对于m同余   
   证明 :a^n-b^k=(a-b)(a^(n-1)+a^(n-2)b.....b^(n-1)) +m|(a-b) ==>m|(a^n-b^n)
  性质9:若 a≡b(mod m1) a≡b(mod m2).... a≡b(mod mi) 则 a≡b(mod [m1,m2,..mi])
   证明:m1 |(a-b) m2|(a-b) ..mi|(a-b)  =>[m1,m2...mi]|(a-b) (因为 a-b里面含了 m集合的所有因子和每个因子的最大个数)
  推论 m1,m2..mi两两互质 则 a≡b(mod m1m2..mi);
 
  定义 : X 代表 M 简化剩余系  个数φ(M)  (简化剩余系的含义,请另查资料)
         Y 代表 N 简化剩余系   个数φ(N)
         xi 代表X的元素 yj代表Y的元素 

  下面证明: φ(MN)=φ(M)φ(N) 其中(M,N)=1
  我们需要证明 
  A: (xiN+yjM,MN)=1, 且当i,j取遍所有值时,xiN+yjM 均不在 MN 的同一剩余类中
    这可以说明 xiN+yjM 能表示 MN 的 φ(M)φ(N) 个不同的剩余类
  B: xiN+yjM 确实可以代表 MN 的简化剩余系的每个元素
    这可以说明 MN 的简化剩余系大小就是 φ(M)φ(N)

A 的证明:
Step 1,
(xi,M)=1; =>(性质5)(xiN,M)=1; => (性质4 yiM≡0(modM))(xiN+yiM,M)=1; ....1 
(yi,N)=1; =>(性质5)(yiM,N)=1; => (性质4 xiN≡0(modN))(yiM+xiN,N)=1; ....2 
由 1,2 => (性质5 (xiN+yiM,M)=1,(1,N)=1)(xiN+yiM,MN)=1; 

Step 2,
使用反证法,我们假设:
存在两个有序二元组 (i,j) ≠ (k,l),使得 xiN+yjM 和 xkN+ylM 落在 MN 的同一剩余类中,即 xiN+yjM≡xkN+ylM (mod MN) 。
由 xiN+yjM≡xkN+ylM (mod MN)
=> xiN+yjM≡xkN+ylM (mod M) 
=> xiN≡xkN (mod M) 
由性质 7 => xi≡xk (mod M)
因为在 M 的简化剩余系中,不存在两个对 M 同余的数,所以 i=k 。同理,j=l。
即(i,j) = (k,l),与假设矛盾,所以,当i,j取遍所有值时,xiN+yjM 均不在 MN 的同一剩余类中 

B 的证明: 
设 Z 是 MN 的简化剩余系的集合的任意某个元素 
由 (N,M)=1 => 存在 x0,y0,使得 Mx0+Ny0=1 => Mx0Z+Ny0Z=Z 
=> 存在 p,q,使得 Mp+Nq=Z 
由 (Z,MN)=1; => (Mp+Nq,M)=1; => (Nq,M)=1; => (q,M)=1 
=> 存在i,使得 xi≡q (mod M) => xiN=Nq (mod MN)   ....1
同理可得 (p,N)=1 => 存在j,使得 yj≡p (mod N) => yjM=Mp (mod MN)   ....2
两式相加可得,Mp+Nq≡xiN+yjM (mod MN) => Z≡xiN+yjM (mod MN) 
所以 MN 的简化剩余系每个元素都可以用 xiN+yjM 表示。

综上, φ(MN)=φ(M)φ(N) 成立。
http://www.cppblog.com/RyanWang/archive/2009/07/19/90512.aspx?opt=admin
E(x)表示比x小的且与x互质的正整数的个数。
*若p是素数,E(p)=p-1。
*E(p^k)=p^k-p^(k-1)=(p-1)*P^(k-1)
证:令n=p^k,小于n的正整数数共有n-1即(p^k-1)个,其中与p不质的数共[p^(k-1)-1]个(分别为1*p,2*p,3*p...p(p^(k-1)-1))。
所以E(p^k)=(p^k-1)-(p^(k-1)-1)=p^k-p^(k-1).得证。
*若ab互质,则E(a*b)=E(a)*E(b),欧拉函数是积性函数.
*对任意数n都可以唯一分解成n=p1^a1*p2^a2*p3^a3*...*pn^an(pi为素数).
则E(n)=E(p1^a1)*E(p2^a2)*E(p3^a3)*...*E(pn^an)     
      =(p1-1)*p1^(a1-1)*(p2-1)*p2^(a2-1)*...*(pn-1)*pn^(an-1)
      =(p1^a1*p2^a2*p3^a3*...*pn^an)*[(p1-1)*(p2-1)*(p3-1)*...*(pn-1)]/(p1*p2*p3*...*pn)
      =n*(1-1/p1)*(1-1/p2)*...*(1-1/pn)
* E(p^k)    =(p-1)*p^(k-1)=(p-1)*p^(k-2)*p
  E(p^(k-1))=(p-1)*p^(k-2)
->当k>1时,E(p^k)=E(p*p^(k-1))=E(p^(k-1))*p.
  (当k=1时,E(p)=p-1.)
  由上式: 设P是素数,
  若p是x的约数,则E(x*p)=E(x)*p.
  若p不是x的约数,则E(x*p)=E(x)*E(p)=E(x)*(p-1). 

小记:


d=gcd(a,b);  a'd=a b'd=b (a',b')=1
要 d=gcd (b,a-qb) 要成立  则(b',a'-qb')=1
证明 (b',a'-qb')=1;
假设 b',a'-qb' 不互质 则 设 b'=kd' a'-qb'=ld' d'>1
则 a'-q(kd')=ld' a'=(qk+l)d' 与 (a',b')矛盾 

问题描述:
给出一个N,求1..N中与N互质的数的和

if gcd(n,i)=1 then gcd(n,n-i)=1 (1<=i<=n)

反证法:
如果存在K!=1使gcd(n,n-i)=k,那么(n-i)%k==0
而n%k=0
那么必须保证i%k=0
k是n的因子,如果i%k=0那么gcd(n,i)=k,矛盾出现;
于是问题变的非常简单
ANS=N*phi(N)/2
i,n-i总是成对出现,并且和是n
于是可能就有人问了,如果存在n-i=i那不是重复计算?
答案是不会
因为:
n=2*i->i=n/2
1.如果n是奇数,那么n!=2*i,自然也不存在n-i=i和重复计算之说
2.如果n是偶数,n=2*i成立,gcd(n,n/2)必然为n的一个因子,这个因子为1当且仅当n==2
于是对于n>2的偶数,绝对不存在gcd(n,n/2)=1所以更别说什么重复计算了
对于n==2
ans=2*1/2=1
正好也满足
#include 
int phi[40010],not_prime[40010],prime[4500],tot=0;
void linear_shaker(int x){
	int i,j;
	for(i=2;i<=x;i++){
		if(!not_prime[i])prime[++tot]=i,phi[i]=i-1;
		for(j=1;prime[j]*i<=x;j++){
			not_prime[i*prime[j]]=1;
			if(i%prime[j]==0){
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}else phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}
int main(){
	int n,i,ans=3;
	scanf("%d",&n);
	if(n==1)printf("1\n");
	else if(n==2)printf("3\n");
	else{
		linear_shaker(n);
		for(i=2;i<=n-1;i++)ans+=phi[i]*2;
		printf("%d\n",ans);
	}
	return 0;
}

方法四:莫比乌斯反演

Accepted 1152 kb 36 ms C++/Edit 658 B

//莫比乌斯反演公式摘自https://blog.csdn.net/u011590573/article/details/81416298找了好久好久2019-12-12 22:16

bzoj 2190 //2190: [SDOI2008]仪仗队 欧拉函数/容斥原理/线性筛+欧拉函数/莫比乌斯反演/莫比乌斯反演+分块+前缀和_第7张图片

//莫比乌斯反演的通俗理解,此文https://blog.sengxian.com/algorithms/mobius-inversion-formula介绍得不错,摘抄如下

//此文https://www.luogu.com.cn/problemnew/solution/P2158?page=8   作者: DennyQi 更新时间: 2018-10-06 20:03 思路不错,摘抄如下

bzoj 2190 //2190: [SDOI2008]仪仗队 欧拉函数/容斥原理/线性筛+欧拉函数/莫比乌斯反演/莫比乌斯反演+分块+前缀和_第8张图片

+2是因为(1,0),(0,1)

//样例通过,提交AC.2019-12-12 22:58

#include 
#define maxn 40010
int not_prime[maxn],prime[4500],tot=0,mu[maxn];
void linear_shaker(int x){
	int i,j;
	mu[1]=1;
	for(i=2;i<=x;i++){
		if(!not_prime[i])prime[++tot]=i,mu[i]=-1;
		for(j=1;i*prime[j]<=x;j++){
			not_prime[i*prime[j]]=1;
			if(i%prime[j]==0){
				mu[i*prime[j]]=0;//此处错写成mu[i]=0;
				break;
			}
			mu[i*prime[j]]=-mu[i];
		}
	}
}
int main(){
	int n,i,ans;
	scanf("%d",&n),n--;
	if(n==0)printf("0\n");
	else{
		linear_shaker(n),ans=2;
		for(i=1;i<=n;i++)
			ans+=mu[i]*(n/i)*(n/i);
		printf("%d\n",ans);
	}
	return 0;
}

方法五:莫比乌斯反演+分块+前缀和

Accepted 1308 kb 32 ms C++/Edit 776 B

//关于分块,此文https://blog.sengxian.com/algorithms/mobius-inversion-formula介绍得不错

//样例通过,提交AC.2019-12-13

#include 
#define maxn 40010
int not_prime[maxn],prime[4500],tot=0,mu[maxn],sum[maxn];
void linear_shaker(int x){
	int i,j;
	mu[1]=1,sum[0]=0,sum[1]=1;
	for(i=2;i<=x;i++){
		if(!not_prime[i])prime[++tot]=i,mu[i]=-1;
		for(j=1;i*prime[j]<=x;j++){
			not_prime[i*prime[j]]=1;
			if(i%prime[j]==0){
				mu[i*prime[j]]=0;//此处错写成mu[i]=0;
				break;
			}
			mu[i*prime[j]]=-mu[i];
		}
		sum[i]=sum[i-1]+mu[i];//前缀和
	}
}
int main(){
	int n,i,ans,l,r;
	scanf("%d",&n),n--;
	if(n==0)printf("0\n");
	else{
		linear_shaker(n),ans=2;
		for(l=1;l<=n;l=r+1){//分块
			r=n/(n/l);
			ans+=(sum[r]-sum[l-1])*(n/r)*(n/r);
		}
		printf("%d\n",ans);
	}
	return 0;
}

 

你可能感兴趣的:(跟着大佬学算法)