木板——题解

题目大意

给出一个边长为N的正方形,左下角为坐标原点建立二维直角坐标系
求用两条线段将其分成4个直角三角形的方案数(两条线段互相垂直,且线段与正方形的边的交点要求为整点)
如图:
木板——题解_第1张图片
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)+((Nx)2+(Ny)2)=N2+x2 ( N 2 + y 2 ) + ( ( N − x ) 2 + ( N − y ) 2 ) = N 2 + x 2
化简一下,易得
x=Nyy2N x = N − y − y 2 N
然后考场上的我敏(sha)锐(bi)地发现,不用枚举每个y,我们可以直接枚举
y2=kN=kpqii 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,yy
    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
则有
pqi2i|x ∏ p i ⌈ q i 2 ⌉ | x
所以答案为
Npqi2i N ∏ p i ⌈ q i 2 ⌉

(pqi2i)1 ( ∏ p i ⌊ q i 2 ⌋ ) − 1
就可以写个稳定的 O(NlogN) 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;
}

你可能感兴趣的:(考试,简单数论,玄学,线性筛)