1. 程式人生 > >[POI2002][HAOI2007]反素數

[POI2002][HAOI2007]反素數

long 分解 學習 cstring \n 計算 ora ack 我們

題目描述

  對於任何正整數x,其約數的個數記作g(x)。例如g(1)=1、g(6)=4。如果某個正整數x滿足:g(x)>g(i) 0<i<x,則稱x為反質數。例如,整數1,2,4,6等都是反質數。現在給定一個數N,你能求出不超過N的最大的反質數麽?

此題以前學習過,此次刷POI又一次碰到了,我們看到要找到不超過N的最大反質數,我們就可以將問題轉化。

在一開始的時候我將問題轉化錯了,我將問題轉化為找到不大於N的中約數最多的數中的的最大的那個。我們考慮它為什麽它不對。

  令 ∀ x<y<=N 使g(x)=g(y)

    那麽 那麽我們發現對於y來說,當i=x的時候,不滿足g(x)>g(i)的要求,若將題設改為找到不大於N的中約數最多的數中的的最小的那個就會滿足要求

由公式n= a[1]^k[1] * a[2]^k[2] *…..a[n]^k[n],約數個數 t=(k[1]+1)(k[2]+1)…..*(k[n]+1)可得出約數個數計算的方法由此我們可以進行搜索。

這裏對於我們的搜索有一個小剪枝(不加會TLE,實測有效):

  一個數由許多質數的某次方相乘得到,難麽滿足題設的數所分解的較大的質數的指數一定小於較小的質數的指數

證明也很簡單:如果較大的質數的指數大於較小的質數的指數,那麽我們將兩個質數的指數交換會使最終乘起來的數字變小,卻不會使該數的約數個數變少,至此原剪枝得證

實現如下:

#include <algorithm>
#include 
<iostream> #include <cmath> #include <cstring> #include <map> #include <string> #include <vector> #include <queue> #include <stack> #include <cstdio> #include <cstdlib> using namespace std; typedef long long ll; const ll su[16]={1
,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47}; inline ll read() { register ll p(1),a(0);register char ch=getchar(); while((ch<0&&ch>9)&&ch!=-) ch=getchar(); if(ch==-) p=-1,ch=getchar(); while(ch>=0&&ch<=9) a=a*10+ch-48,ch=getchar(); return a*p; } inline ll min(ll x,ll y){return x>y?y:x;} ll n,ans=0x3f3f3f3f,maxn=0,ke=0; void dfs(ll xx,ll pre,ll num,ll jl) { ll ji=0; if(num>maxn) ans=xx,maxn=num; else if(num==maxn) ans=min(ans,xx); if(pre==16) return; while(ji<=jl&&xx<=n) { dfs(xx,pre+1,num*(ji+1),ji); xx*=su[pre];++ji; } } int main() { n=read(); dfs(1,1,1,30); printf("%lld\n",ans); return 0; }

[POI2002][HAOI2007]反素數