给出一个边长为N的正方形,左下角为坐标原点建立二维直角坐标系
求用两条线段将其分成4个直角三角形的方案数(两条线段互相垂直,且线段与正方形的边的交点要求为整点)
如图:
1<=N<=1014 1 <= N <= 10 14
自己果然数学太差了。。
首先,我们有个 O(N) O ( N ) 的想法:枚举y,检查x是否满足 1<=x<N 1 <= x < N
利用勾股定理,易得:
AE2+EF2=AF2 A E 2 + E F 2 = A F 2
即
(N2+y2)+((N−x)2+(N−y)2)=N2+x2 ( N 2 + y 2 ) + ( ( N − x ) 2 + ( N − y ) 2 ) = N 2 + x 2
化简一下,易得
x=N−y−y2N x = N − y − y 2 N
然后考场上的我敏(sha)锐(bi)地发现,不用枚举每个y,我们可以直接枚举
y2=k∗N=k∗∏pqii y 2 = k ∗ N = k ∗ ∏ p i q i
先将奇数的 qi q i 补成偶数,再DFS枚举乘 p2j p j 2
感性理解(期望)情况不会太多
然后tep数组忘开long long,70分。。
实际效率还是挺高的。。复杂度 O(能快速过) O ( 能 快 速 过 )
#pragma GCC optimize(2)
#include
#include
#define LL long long
#define gt() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int maxp=(1e7)+5,N=(1e7);
int p[maxp],k,num[maxp],lst[maxp];bool vis[maxp];LL n,Ans,sqr[maxp],tep[maxp];
static char buf[1000000],*p1=buf,*p2=buf;
LL read(){
LL ret=0;char ch=gt();
while(ch<'0'||ch>'9') ch=gt();
while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=gt();
return ret;
}
void make(){
for(int i=2;i<=N;i+=(i&1)+1){
if(!vis[i]) p[++p[0]]=i,sqr[p[0]]=(LL)i*i;
for(int j=1;j<=p[0];j++){
if(p[j]>N/i) break;
vis[p[j]*i]=1;
if(!(i%p[j])) break;
}
}
p[p[0]+1]=maxp;
}
inline LL Pow(LL a,LL b){
LL s=1,w=a;
while(b){
if(b&1) s*=w;
w*=w,b>>=1;
}
return s;
}
void DFS(int x,LL r,LL y){//当前枚举到r*N,y为y
if(!x){if(1<=n-y+r&&n-y+rreturn;}
DFS(x-1,r,y);
while(1) if(y<=n/p[x]&&r<=n/sqr[x]) DFS(x-1,(r*=sqr[x]),(y*=p[x]));else break;
}
int main(){
make();
for(n=read();n;n=read()){
LL x=n,i,ti;k=0;
for(i=1,ti=sqrt(x);p[i]<=ti;i++){
if(!(x%p[i])) tep[++k]=p[i],num[k]=0;else continue;
do num[k]++,x/=p[i];while(!(x%p[i]));
lst[k]=num[k];
}
if(x!=1) tep[++k]=x,lst[k]=num[k]=1;
LL A=1,B=1;
for(int j=1;j<=k;j++) if(num[j]&1) num[j]++,A*=tep[j];
for(int j=1;j<=k;j++) B*=Pow(tep[j],num[j]>>1);
Ans=0;
DFS(i-1,A,B);
printf("%lld\n",Ans<<3);
}
return 0;
}
然后考后用数学的想法去搞:
这题不就是求 N|y2(1<=y<N) N | y 2 ( 1 <= y < N ) 的个数吗?
设 N=∏pqii N = ∏ p i q i
则有
∏p⌈qi2⌉i|x ∏ p i ⌈ q i 2 ⌉ | x
所以答案为
N∏p⌈qi2⌉i N ∏ p i ⌈ q i 2 ⌉
即
(∏p⌊qi2⌋i)−1 ( ∏ p i ⌊ q i 2 ⌋ ) − 1
就可以写个稳定的 O(N−−√∗logN) O ( N ∗ log N ) 左右的算法了。。
//#pragma GCC optimize(2)
#include
#include
#define LL long long
#define gt() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int maxp=(1e7)+5,N=(1e7);
int p[maxp];bool vis[maxp];LL n,Ans;
static char buf[1000000],*p1=buf,*p2=buf;
LL read(){
LL ret=0;char ch=gt();
while(ch<'0'||ch>'9') ch=gt();
while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=gt();
return ret;
}
void make(){
for(int i=2;i<=N;i+=(i&1)+1){
if(!vis[i]) p[++p[0]]=i;
for(int j=1;j<=p[0];j++){
if(p[j]>N/i) break;
vis[p[j]*i]=1;
if(!(i%p[j])) break;
}
}
p[p[0]+1]=maxp;
}
inline LL Pow(LL a,LL b){
LL s=1,w=a;
while(b){
if(b&1) s*=w;
w*=w,b>>=1;
}
return s;
}
int main(){
make();
for(n=read();n;n=read()){
LL x=n;Ans=1;
for(int i=1,ti=sqrt(n);x!=1&&p[i]<=ti;i++){
int cnt=0;
while(!(x%p[i])) cnt++,x/=p[i];
if(cnt) Ans*=Pow(p[i],cnt>>1);
}
printf("%lld\n",--Ans<<3);
}
return 0;
}