题目链接(是的我越来越懒了。。)
题目大意及数据范围:
数据范围很大。“最小”二字让我们考虑二分,但是上界...不会爆long long让你写高精吧?
我们可以发现,∑ai一定满足条件,所以上界是1e13。为什么满足呢?因为C(n+m,n)是整数,所以n!| ( (n+m)!/m! ) ,所以满足。
然后问题只剩下怎么满足判断整除了。数很大,肯定要分解。n!的分解容易解决,因为我们只需考虑1e7范围内的质数即可,每次计算效率约为1e7/ln,总的复杂度能接受。所以只需解决各ai!如何分解。显然不能像n那个挨个质数地试,冗余过多,我们得考虑把各ai和在一起。既然都是阶乘,我们只需考虑每个数x对几个ai产生贡献即可(满足x≤ai的ai有几个),挨个将x分解的复杂度我们还是可以接受的(毕竟CF的机子,还开5s)。
总时间复杂度O(ai log(max{ai}) )
#include <bits/stdc++.h> using namespace std; #define rep(i,l,r) for(int i=l;i<=r;++i) #define per(i,r,l) for(int i=r;i>=l;--i) typedef long long ll; const int N=1e6+3,V=1e7; int n,a[N]; ll ans[V+3];//ans[j] namespace Euler{ bool h[V+3]; int pr[N],len,miz[V+3];//miz[x]=no void sieve(){ rep(i,2,V){ if(!h[i]){ pr[++len]=i; miz[i]=len; } int val; for(int j=1;j<=len&&(val=pr[j]*i)<=V;++j){ h[val]=1; miz[val]=j; if(i%pr[j]==0) break; } } } void dvd(int x,int ct){//divide x to ans ans[no] if(x==1) return ; int no=miz[x],P=pr[no]; while(x%P==0){ ans[no]+=ct; x/=P; } dvd(x,ct); } void calc(){ int L=1; rep(i,2,V){ while(a[L]<i&&L<=n) ++L; //ct=n-L+1 dvd(i,n-L+1); } } bool ck(ll val){ rep(j,1,len){ ll sum=pr[j],P=pr[j],ad=0; while(sum<=val){ ad+=(val/sum); sum*=P; } if(ad<ans[j]) return 0; } return 1; } } using Euler::sieve; using Euler::calc; using Euler::ck; int main(){ sieve(); scanf("%d",&n); rep(i,1,n) scanf("%d",&a[i]); sort(a+1,a+1+n); calc(); ll L=1,R=1e13; while(L<R){ ll mid=(L+R)>>1; if(ck(mid)) R=mid; else L=mid+1; } printf("%lld\n",L); return 0; }
日常写错……欢迎指正
QQ:2960005671
邮箱:bleavescoder@163.com