1. 程式人生 > >RGCDQ(線段樹+數論)

RGCDQ(線段樹+數論)

func prim std stdlib.h cli size pop 之間 分析

題意:求n和m之間的全部數的素因子個數的最大gcd值。

分析:這題好惡心。看著就是一顆線段樹。但本題有一定的規律,我也是後來才發現,我還沒推出這個規律。就不說了,就用純線段樹解答吧。

由於個點數都小於1000000所以素因子個數不會超過7個所以建一個線段樹,最以下一層是每一個節點的素因子個數為1,2。3,4,5,6。7的有多少個,父節點求和。終於查詢的是n到m之間有多少個1,2,3。4。5,6,7然後存在就求一下gcd著最大就好了

本題最重要的時間和空間。顯然線段數中的點不會非常大,所以採用short類型

代碼例如以下:

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <math.h>
#include <vector>
#include <string>
#include <utility>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <functional>

using namespace std;
int gcd(int a,int b){
    return (b==0)?a:gcd(b,a%b);
}
const int N=100000;
int prime[N]={0};
int num_prime=0;
bool isNotPrime[N]={1,1};
void su1(){
    for(long i = 2 ; i < N ; i ++){
        if(!isNotPrime[i])
        prime[num_prime++]=i;
        for(long j = 0 ; j < num_prime &&i*prime[j]<N ;j ++) {
            isNotPrime[i * prime[j]] = 1;
            if( !(i % prime[j]))break;
        }
    }
}
int prime_solve(int n){
    int k=0;
    for(int i=0;i<num_prime&&prime[i]*prime[i]<=n;i++){
//        cout<<prime[i]<<endl;
        if(n%prime[i]==0){
            while(n%prime[i]==0){
                n/=prime[i];
            }
            k++;
        }
    }
    if(n!=1)k++;
    return k;
}//素因子分解求n的素因子個數
short a[4000005][8];
void updat(int id,int j,int l,int r,int mid){
    if(l==r){
        a[mid][j]=1;
        return;
    }
    int i=(l+r)>>1;
    if(id<=i)updat(id,j,l,i,2*mid);
    else updat(id,j,i+1,r,2*mid+1);
    a[mid][j]=a[2*mid][j]+a[2*mid+1][j];
}
int sum[8];
void su(int l,int r,int mid,int ll,int rr){
    if(l>=ll&&r<=rr){
        for(int i=1;i<=7;i++)
        sum[i]+=a[mid][i];
        return;
    }
    int i=(l+r)>>1;
    if(ll<=i)su(l,i,2*mid,ll,rr);
    if(rr>i)su(i+1,r,2*mid+1,ll,rr);
}//建樹,求和,這是重點
int main(){
    memset(a,0,sizeof(a));
    su1();
//    for(int i=1;i<=100;i++){
//    if(isNotPrime[i])
//    cout<<i<<" "<<prime_solve(i)<<endl;;
//    }
//    cout<<endl;
    for(int i=2;i<=1000005;i++){
        int d=prime_solve(i);
        updat(i,d,2,1000005,1);
    }
    int n,m;
    int t;
    cin>>t;
    while(t--){
        scanf("%d%d",&n,&m);
        memset(sum,0,sizeof(sum));
        su(2,1000005,1,n,m);
        int ans=-1;
        for(int i=1;i<=7;i++)
        for(int j=1;j<=7;j++){
            if(i==j){
                if(sum[i]>1)ans=max(ans,gcd(i,j));
            }
            else{
                if(sum[i]>0&&sum[j]>0)ans=max(ans,gcd(i,j));
            }
        }//這個地方就能夠純暴力了
        printf("%d\n",ans);
    }
    return 0;
}


RGCDQ(線段樹+數論)