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
如图,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
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介绍得不错,摘抄如下
对于求解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思路不错,摘抄如下
/*
去重部分解释如下:
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
//莫比乌斯反演的通俗理解,此文https://blog.sengxian.com/algorithms/mobius-inversion-formula介绍得不错,摘抄如下
//此文https://www.luogu.com.cn/problemnew/solution/P2158?page=8 作者: DennyQi 更新时间: 2018-10-06 20:03 思路不错,摘抄如下
+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;
}