首页 > 代码库 > hdu 3641 数论 二分求符合条件的最小值数学杂题

hdu 3641 数论 二分求符合条件的最小值数学杂题

http://acm.hdu.edu.cn/showproblem.php?pid=3641

学到:
1、二分求符合条件的最小值

/*====================================================
    二分查找符合条件的最小值
======================================================*/
ll solve()
{
     __int64 low = 0, high = INF, mid ;
    while(low <= high){
        mid = (low + high) >> 1;
//         printf("%d&&\n",mid);
        if(C(mid))    high = mid - 1;
        else low = mid + 1;
    }
    return low;
}

2、求x!中prime[i]的个数

            for(ll k=prm[i];k<=x;k*=prm[i])
            {
                sum+=x/k;   ///
                if(sum>=num[prm[i]])break;
            }

牛逼的代码,解释如下:
比如x=16,prm[i]=2,一般的思路是2,4,6,8,10,12,14,16  算出这几个数的因子2的个数和,因为4,8,16等不止包含一个因子,所以不是一重循环就可以搞的

这个代码在O(logn)搞了 方法是  k*=prm[i]  比如包含2的平方这个因子的数是4,8,16,那么第一次k=2的时候已经加过一次1,当k*=2  即k==4的时候,又加了一个1,也就相当于考虑上2的二次,  不错的代码


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <map>
#include <set>
#include <queue>
using namespace std;

#define ls(rt) rt*2
#define rs(rt) rt*2+1
#define ll long long
#define ull unsigned long long
#define rep(i,s,e) for(int i=s;i<e;i++)
#define repe(i,s,e) for(int i=s;i<=e;i++)
#define CL(a,b) memset(a,b,sizeof(a))
#define IN(s) freopen(s,"r",stdin)
#define OUT(s) freopen(s,"w",stdout)
const ll ll_INF = ((ull)(-1))>>1;
const double EPS = 1e-8;
const ll INF = ll_INF/2;//99999999999999999999;///ll_INF;//

const int MAXN =110;
const int N = 110;
const int M = N;
int prmcnt;
bool is[N]; int prm[M];
int getprm(int n){
    int i, j, k = 0;
    int s, e = (int)(sqrt(0.0 + n) + 1);
    memset(is, 1, sizeof(is));
    prm[k++] = 2; is[0] = is[1] = 0;
    for(i = 4; i < n; i += 2) is[i] = 0;
        for(i = 3; i < e; i += 2) if(is[i]) {
            prm[k++] = i;
            for(s = i * 2, j = i * i; j < n; j += s)
                is[j] = 0;
// 因为j是奇数,所以+奇数i后是偶数,不必处理!
        }
    for( ; i < n; i += 2) if(is[i]) prm[k++] = i;
    return k;  // 返回素数的个数
}

ll a[MAXN],b[MAXN];
ll num[MAXN];

ll getnum(ll t, ll pm)
{
    ll ret=0;
    while(t%pm == 0)
    {
        ret++;
        t/=pm;
    }
    return ret;
}

bool C(ll x)
{
    ll sum=0;
    for(int i=0;i<prmcnt;i++)
        if(num[prm[i]])///
        {
            sum=0;
            for(ll k=prm[i];k<=x;k*=prm[i])
            {
                sum+=x/k;   ///
                if(sum>=num[prm[i]])break;
            }
            if(sum<num[prm[i]])return 0;
        }
    return 1;
}

/*ll solve()
{
    ll d=0,up=INF,mid;
    while(up>d+1)
    {
        mid=(d+up)/2;
        if(C(mid))up=mid;
        else d=mid;
    }
    return up;
}*/
ll solve()
{
     __int64 low = 0, high = INF, mid ;
    while(low <= high){
        mid = (low + high) >> 1;
//         printf("%d&&\n",mid);
        if(C(mid))    high = mid - 1;
        else low = mid + 1;
    }
    return low;
}

int main()
{
   //IN("hdu3641.txt");
    prmcnt=getprm(105);
    int ncase;
    int n;
    scanf("%d",&ncase);
    while(ncase--)
    {
        CL(num,0);
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%I64d%I64d",&a[i],&b[i]);
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<prmcnt;j++)
            {
                if(a[i]%prm[j] == 0)
                {
                    num[prm[j]]+=getnum(a[i],prm[j])*b[i];
                }
            }
        }
        printf("%I64d\n",solve());
    }
    return 0;
}