#con5多校联盟 (李主席与阶乘)

http://oj.xjtuacm.com/problem/11/
李主席对10000内的阶乘能倒背如流,有一天学妹请教李主席一个问题,需要对这样一个式子进行化简:
a[i]!乘积/b[i]!
化成c
1
!^e1*c2!^e2…
t
使得c1最大情况下e1最大,
再使得c2最大。。。
tip:
思路大概是这样的:
先质因数分解ai得阶乘。。。再这一部分,3!*9!*5!这种,排序:3!*5!*9!,于是1 2 3出现3次,4 5 两次,6 7 8 9三次,就是可以直接统计出来每个数字自己出现了多少,然后一次质因数分解这个数字(最多10000)就好了
同理对B
a/b可得。。叫做p
显然如果某个指数出现了负数,就说明没法凑出答案要的东西,然后在思考,如何使得c1最大,这个时候考虑c1最大是第一个大于10000得素数-1(记为now)(下一个素数肯定不会出现,也就不可能能分解出来他的阶乘) 依次递减枚举(now–),每次就相当于把now这个质因数分解,相应的指数减掉就是(now-1)!
现在需要知道能凑出来多少个(now-1)!,可能凑不出来,就是(now-1)!每个指数/p得相应指数,这维护成最小堆就刚刚好,堆顶就是能凑成多少个,每次now减少的时候heap_down一下,如果可以我们堆里所有的元素都要减这个堆头,这样复杂毒比较高,考虑在外面设置累加器sub,堆得值-sub才是真正多少,但是这时候就出现了问题,当我now变成now-1的时候,怎么能保证值-sub还是对的(比得数减小了)
考虑维护一个pre_a 代表上一次这个质因数所需质数变化之后还剩多少这个质因数,-外面sub*上次变化后得对这个因数得需要,得出真实值还要+sub和堆里别的统一
pre_a就减去(减少多少个这个因数)*之前的sub sub是成功的,对于每个成功得,都用了比now-1多得这部分,减掉之后就保证了正确性

heap[_index[j]].h_cnt = (heap[_index[j]].pre_a-SUB * tt)/pfac_b[j] + SUB;
heap[_index[j]].pre_a = heap[_index[j]].pre_a-cj*SUB;
 heap[_index[j]].n_cnt = pfac_b[j];

其余得小chick:
再统计每个数得质因子得时候,(我们的目的是对于任意数字k,快速找到其所有质因子)可以:
首先枚举每个质数,然后统计每个数有多少个质因数,然后给他这么多得空间放置他的质因数
然后对于每个素数,所有有他作为银子的数都给个位置,把它放进去:

for(int i = 0 ; i < cnt ; i++){
        for(int j = prime[i] ; j <= prime_max ; j+= prime[i]){
            countprimefac[j]++;
            cont[j]++;
        }
    }
    for(int i = 2; i <= prime_max ; i++)
        countprimefac[i] += countprimefac[i-1],cont[i] += cont[i-1];

    for(int i = 0 ; i < cnt ;i++)
    for(int j = prime[i] ; j <= prime_max ; j+= prime[i]){
        primefac[countprimefac[j]--] = i;
;
    }

枚举时候“

for(int j = cont[i-1]+1; j <= cont[i];j++){//位置
         while(tmp % prime[primefac[j]] == 0){
                if(f)   pfac_a[primefac[j]] += num;
                if(!f)  pfac_b[primefac[j]] += num;

                tmp /= prime[primefac[j]];
            }
        }

素数筛:

  for(int i = 2; i < maxn ; i ++){
        if(notprim[i] == 0){
            prime[cnt++] = i;
            to[i] = cnt-1;
            if(i > 10000){
                prime_max = i;
                break;
            }
            for(int j = 2 * i ; j < maxn; j += i){
                notprim[j] = 1;
            }
        }
    }
/*
1.
首先打素数表,
对于每个a1!*a2!*...an!把a1a2...an排序,里面每个数字个数都知道啦(On)
然后对于每个1~n做质因数分解,就知道了指数得值(Osqrt(n))
以上处理了分子和分母,相减,如果有负数直接return -1,剩下就是原式值每个质因数得指数了

2.
对于大于10000得第一个质数-1,是可能出现的最大数满足阶乘可能够上面得值,设他是n(sqrt(n)*n)得时间分解n!得质因数
然后n依次变小,看能不能分解出来,能的话堆里所有数都要-多少个n分解,这个在外面放累加器ouT
堆是个维护(每个因数多少个/需要多少个)得最小堆,(结点还要和存本来多少个和是哪个质数)
当n--时候,相当于n得质因数分解出来的那些质数得指数剪掉分解出来的,堆里面得值需要变化,log(pi(n))找到,
-ouT的值/需要 +ouT 放回堆里//down——heap
*/

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <queue>

using namespace std;
const int maxn = 10010;
const int maxm = 10100;
const int mm = 1500+10;
const int INF = 1e9;
int prime[mm] ;
int cnt,n,m,a[maxm],b[maxm],CNT,SUB,cntans;

int ansa[maxn],ansb[maxn],pfac_a[mm],pfac_b[mm],prime_max;
int c[maxm],e[maxm],_index[maxn],Size,to[maxn],notprim[maxn];
int cont[maxn*10],countprimefac[maxn*10],primefac[maxn*10];
struct node{
    int pre_a;//最开始得式子种有多少个该素因子
    int h_num;//中间值
    int h_cnt;//能搞出多少个当前得c
    int n_cnt;//n!需要多少个该素因子
    int h_prm;//这是哪个因子
}heap[maxm];

void init(){
    SUB = 0;

    for(int i = 1; i <= n ; i++){
        scanf("%d",&a[i]);

    }
    for(int j = 1; j <= m ; j++){
        scanf("%d",&b[j]);

    }
    for(int i = 0 ; i < mm ; i++)
        pfac_a[i] = pfac_b[i] = 0;
    for(int i = 0 ; i < maxn ; i++)
        ansa[i] = ansb[i] = 0;
}

void getprim(){
    for(int i = 2; i < maxn ; i ++){
        if(notprim[i] == 0){
            prime[cnt++] = i;
            to[i] = cnt-1;
            if(i > 10000){
                prime_max = i;
                break;
            }
            for(int j = 2 * i ; j < maxn; j += i){
                notprim[j] = 1;
            }
        }
    }
    for(int i = 0 ; i < cnt ; i++){
        for(int j = prime[i] ; j <= prime_max ; j+= prime[i]){
            countprimefac[j]++;
            cont[j]++;
        }
    }
    for(int i = 2; i <= prime_max ; i++)
        countprimefac[i] += countprimefac[i-1],cont[i] += cont[i-1];

    for(int i = 0 ; i < cnt ;i++)
    for(int j = prime[i] ; j <= prime_max ; j+= prime[i]){
        primefac[countprimefac[j]--] = i;
    }

}
void getfactor(int i,int num,int f){
        int tmp = i;
        for(int j = cont[i-1]+1; j <= cont[i];j++){
                //cout << "i = "<<i<<" as  "<<prime[primefac[j]]<<endl;
            while(tmp % prime[primefac[j]] == 0){
                if(f)   pfac_a[primefac[j]] += num;
                if(!f)  pfac_b[primefac[j]] += num;

                tmp /= prime[primefac[j]];
            }
        }
   // cout << "tmp "<<tmp<<"  to"<<to[tmp] <<endl;
}
//3 3 3 4 6 10 15 20
void sov(){
    sort(a+1,a+1+n);
    sort(b+1,b+1+m);
    for(int i = 1; i <= n ;i++){
        for(int k =  a[i-1]+1 ; k <= a[i] ; k++){
            ansa[k] = n-(i-1);
            //printf("ansa[%d] = %d\n",k,ansa[k]);
        }
    }
    for(int k = 1; k < maxn; k++){
        if(ansa[k]){
            getfactor(k,ansa[k],1);
            //printf(" k = %d  2: %d\n",k,pfac_a[0]);
        }
    }
    for(int i = 1; i <= m ;i++){
        for(int k =  b[i-1]+1 ; k <= b[i] ; k++){
            ansb[k] = m-(i-1);
           // printf("ansb[%d] = %d\n",k,ansb[k]);
        }
    }
    for(int k = 1; k < maxn; k++){
        if(ansb[k])
            getfactor(k,ansb[k],0);
    }

}
int check(){
    for(int k = 0; k < cnt ; k++){
        if(pfac_a[k] < pfac_b[k]){
            printf("-1\n");
            return 0;
        }
        if(pfac_a[k] && pfac_b[k]){
           // cout <<"k = "<<prime[k]<<" a =  "<<pfac_a[k] <<"  b :"<<pfac_b[k]<<endl;
            pfac_a[k] -= pfac_b[k];
            if(pfac_a[k] < 0){
                printf("-1\n");
                return 0;
            }
        }

    }
//    for(int k = 0; k < cnt ; k++){
//        if(pfac_a[k])
//            printf("pfac_a[%d] = %d\n",prime[k],pfac_a[k]);
//    }
    return 1;
}

void heap_up(int ind){
    while(ind > 1){
        if(heap[ind].h_cnt < heap[ind/2].h_cnt){
            swap(heap[ind],heap[ind/2]);
            swap(_index[heap[ind].h_prm],_index[heap[ind/2].h_prm]);
        }
        else    break;
        ind /= 2;
    }
}

void heap_down(int ind){
    while(ind * 2 <= Size){
        ind *= 2;
        if(ind < Size && heap[ind].h_cnt > heap[ind +1].h_cnt){
            ++ind;
        }
        if(heap[ind].h_cnt < heap[ind/2].h_cnt){
            swap(heap[ind],heap[ind/2]);
            swap(_index[heap[ind].h_prm],_index[heap[ind/2].h_prm]);
        }
        else break;
    }
}

//这个数再a里,b里面是要求得c1(循环减小ing)
void get_heap(){
    Size = 0;
    for(int i = 0; i < cnt ; i++){
        if(pfac_a[i]){
            //printf("a[%d] = %d  b [%d] = %d\n",prime[i],pfac_a[i],prime[i],pfac_b[i]);
            heap[++Size].h_cnt = (pfac_b[i] == 0)? INF:pfac_a[i]/pfac_b[i];
            //printf("heap[%d].cnt = %d\n",Size,heap[Size].h_cnt);

            heap[Size].h_prm = i;
            heap[Size].pre_a = pfac_a[i];
            heap[Size].n_cnt = pfac_b[i];
            _index[i] = Size;
            heap_up(Size);
        }
    }
}

void decrease(int tmp){
    int i = tmp;
    for(int j = cont[i-1]+1; j <= cont[i];j++){
        while(tmp % prime[primefac[j]] == 0){
            pfac_b[primefac[j]]--;
            tmp /= prime[primefac[j]];
        }
    }

}


void findans(int k){
    while(k >= 2){
       // cout <<"k = "<<k<<"  heap[1]  =   "<<heap[1].h_prm<<"  "<<heap[1].h_cnt<<"  sub = "<<SUB<<endl;
        int tmp = k,j;
        if(heap[1].h_cnt > SUB){//可以分解
            c[cntans] = k;
            e[cntans++] = heap[1].h_cnt-SUB;
            SUB += (heap[1].h_cnt-SUB);
        }
        int i = tmp;
        for(int jj = cont[i-1]+1; jj <= cont[i];jj++){//减掉tmp自己得分解
            int cj = 0,tt = pfac_b[primefac[jj]];
            while(tmp % prime[primefac[jj]] == 0){
                cj++;
                tmp /= prime[primefac[jj]];
                pfac_b[primefac[jj]]--;
            }
            j = primefac[jj];

            if(cj){
                if(pfac_b[j]){
                    heap[_index[j]].h_cnt = (heap[_index[j]].pre_a-SUB * tt)/pfac_b[j] + SUB;
                    heap[_index[j]].pre_a = heap[_index[j]].pre_a-cj*SUB;
                    heap[_index[j]].n_cnt = pfac_b[j];
                }

                else
                    heap[_index[j]].h_cnt = INF;

                heap_down(_index[j]);
            }
        }
        k--;
    }
}

int mak_heap(){
    int tmp = prime_max;

    cntans = 0;
    for(int i = 0 ; i < cnt; i++)
        pfac_b[i] = 0;
    for(int i = 1 ; i <= tmp ; i++)
        getfactor(i,1,0);

    while(1){
        int i;
        for(i = cnt-1; i >= 0 ; i--){
            if(pfac_b[i] > pfac_a[i]){
                decrease(tmp);
                --tmp;
                break;
            }
        }
        if(i == -1){
            get_heap();
            break;
        }
        if(tmp == 1){

            printf("0\n");
            return 1;
        }
    }

    findans(tmp);
    return 0;
}

void print(){
    printf("%d\n",cntans);
    for(int i = 0 ; i < cntans; i++){
        printf("%d %d\n",c[i],e[i]);
    }
}

int main(){
    getprim();
    while(~scanf("%d%d",&n,&m)){
        init();
        sov();
        if(check() == 0)    continue;
        if(mak_heap())      continue;
        print();
    }
    return 0;
}
/*
5 6
17 6 10 11 3
5 5 13 7 7 5
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值