1.SDOI2008仪仗队
题意:求
n∗n
n
∗
n
的矩阵中左下点能直接看到的点的个数
n<=40000
n
<=
40000
注意到每条不重合的射线上最多只有一个点能被看见
记左下角为
(0,0)
(
0
,
0
)
那么对于同一射线上的点
(x1,y1)
(
x
1
,
y
1
)
与
(x2,y2)
(
x
2
,
y
2
)
他们的斜率
y1x1=y2x2
y
1
x
1
=
y
2
x
2
都能被约分成互质两数的比值
yx
y
x
故
gcd(x,y)=1
g
c
d
(
x
,
y
)
=
1
的点的个数就是答案
当n>=2时
容易发现
x
x
轴和轴与对角线上一共有3个点
这三条线把矩阵分成了两个关于对角线对称的三角形
而左上三角形的横坐标总是小于等于纵坐标
故我们可以用欧拉函数求得左上三角形中每一行横坐标与纵坐标最大公约数为1的点数
如当y=2 左上三角形这一行对答案的贡献为
ϕ(2)=1
ϕ
(
2
)
=
1
总贡献就为2
提前筛出n以内的欧拉函数值
#include<cstdio>
#include<cstring>
using namespace std;
int p[40007],n;
void getphi(int *phi){
for(int i=2;i<=n;i++){
phi[i]=0;
}
phi[1]=1;
for(int i=2;i<=n;i++)
if(!phi[i]){//质因数 处理所有该数的倍数(乘上(1-1/i) 即
//乘上((i-1)/i)
for(int j=i;j<=n;j+=i){
if(!phi[j]) phi[j]=j;//初始化phi[j]
phi[j]=phi[j]/i*(i-1);//乘上质因数i时的(1-1/i)
}
}
}
int res=0;
int main(){
scanf("%d",&n);
if(n==1) {printf("0");return 0;}
else if(n==2) {printf("3");return 0;}
getphi(p);
res=3;
for(int i=2;i<n;i++){
res+=p[i]*2;
}
printf("%d",res);
return 0;
}
2.SDOI2012 longge的问题
gcd(i,n)==d
g
c
d
(
i
,
n
)
==
d
可以化成
gcd(i/d,n/d)==1
g
c
d
(
i
/
d
,
n
/
d
)
==
1
当
n==0 (mod d)
n
==
0
(
m
o
d
d
)
然后就是求欧拉函数
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<iostream>
#define ll long long
ll phi(ll x){
ll res=x,top=std::sqrt(x);
for(ll i=2;i<=top;i++){
if(x%i==0){
res=res/i*(i-1);
while(x%i==0) x/=i;
}
}
if(x>1) res=res/x*(x-1);
return res;
}
/*
所有gcd(i,n)==1 的和是phi(n)
然后gcd(i,n)==x&&n%x==0时 =>
对答案的贡献为gcd(i/x,n/x)==1
*/
int main(){
ll n;
std::cin>>n;
ll ans=0,top=std::sqrt(n);
for(ll i=1;i<=top;i++){
if(n%i==0)
ans+=i*phi(n/i)+n/i*phi(i);
}
//注意不要漏掉完全平方的情况
if(top*top==n) ans-=phi(top)*top;
printf("%lld\n",ans);
return 0;
}
3.上帝与集合的正确用法
拓展欧拉定理:
当且仅当
用这个式子递归求解即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int T,P;
#define ll long long
ll phi(int x){
ll res=x;
for(int i=2;i*i<=x;i++){
if(x%i==0){
res=res/i*(i-1);
while(x%i==0) x/=i;
}
}
if(x>1) res=res/x*(x-1);
return res;
}
ll ksm(ll a,int b,int mod){
ll res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int solve(int p){
if(p==1) return 0;
return ksm(2,solve(phi(p))+phi(p),p);
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&P);
printf("%d\n",solve(P));
}
return 0;
}
附:欧拉函数的线性筛法
void get_phi(int *phi){//线性筛欧拉函数
phi[1] = 1;
for (int i = 2; i <= maxn;i++){
if(!phi[i]){
phi[i] = i - 1;
prime[++tot] = i;
}
for (int j = 1; j <= tot;j++){
if(i*prime[j]>maxn)
break;
if(!(i%prime[j])){
//特殊性质1:i%p==0 p为质数 phi(i*p)=p*phi(i)
phi[i * prime[j]] = prime[j] * phi[i];
break;
//保证只被最小的质因数筛去一次
}else{
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
//非完全积性函数 当i⊥j phi(i*j)=phi(i)phi(j);
//素数的欧拉函数值为其本身值-1
}
}
}
}