Codeforces1699E Three Days Grace【数学】【DP】
分析:
一开始觉得是二分答案,发现行不通之后改为枚举最小值。
现在我将这若干个数分解,假设分解完之后得到的最小值为$i$,那么我就是要在最小值为$i$的基础上尽量最小化分解的各数的最大值。
考虑DP:设$f[x][i]$表示在分解结果最小值大于等于$i$的情况下,将$x$分解得到的最大值最小是多少。可以得到如下转移:
$$f[x][i] = min(f[\frac{x}{i}][i],f[x][i+1])\quad where\ i\ is\ a\ factor\ of\ x$$
或者
$$f[x][i] = f[x][i+1]\quad i\ is\ not\ x's\ factor$$
注意到这样转移状态数太多了,但是我们发现,只有当$i$是$x$的因子时,$f[x][i]$的值才会发生变化,所以转移可以进一步优化,只留$n\ln n$个有用的状态。
接着,答案就是对每个枚举的$i$,求$f[a[x]][i]$的最大值即可。
这个求最大值的过程如果倒着用可删堆维护会爆空间,所以考虑正着来。先倒着预处理出来$i$等于多少时,$f[x][i]$会发生变化就行了。(参考我代码里的update部分)
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 1050000; 5 const int maxm = 5050000; 6 7 int n,m; 8 int a[maxn]; 9 int suf[maxm]; 10 int tab[maxm]; 11 int update[maxm]; 12 13 struct Priority_Queue{ 14 priority_queue<int,vector<int>,less<int> > pq,del; 15 void Push(int now){pq.push(now);} 16 void Del(int now){del.push(now);} 17 int Top(){ 18 while(!del.empty() && pq.top() == del.top()){ 19 pq.pop(); 20 del.pop(); 21 } 22 return pq.top(); 23 } 24 void clear(){ 25 while(!pq.empty()) pq.pop(); 26 while(!del.empty()) del.pop(); 27 } 28 }Q; 29 30 int main(){ 31 int t; scanf("%d",&t); 32 while(t--){ 33 scanf("%d%d",&n,&m); 34 int minn = 1e9,maxx = 0; 35 Q.clear(); 36 for(int i=1;i<=n;i++) { 37 scanf("%d",&a[i]); 38 minn = min(minn,a[i]); 39 maxx = max(maxx,a[i]); 40 } 41 for(int i=1;i<=maxx;i++){ 42 suf[i] = i; 43 tab[i] = 0; 44 update[i] = 0; 45 } 46 for(int i=1;i<=n;i++) tab[a[i]]=1; 47 int ans = 1e9; 48 for(int i=maxx;i>=2;i--){ 49 for(int j=i;1ll*j*i<=maxx;j++){ 50 if(tab[i*j]) update[i+1] = max(update[i+1],suf[i*j]); 51 suf[i*j] = min(suf[i*j],suf[j]); 52 } 53 } 54 for(int i=1;i<=maxx;i++){ 55 if(tab[i]){ 56 update[1] = max(update[1],suf[i]); 57 } 58 } 59 int tot = 0; 60 for(int i=1;i<=minn;i++){ 61 tot = max(tot,update[i]); 62 ans = min(ans,tot-i); 63 } 64 printf("%d\n",ans); 65 } 66 }