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
*/