元素选择题解

· · 题解

Subtask 1

暴搜。

Subtask 2

dp_i 表示 n=i 时需要操作的次数,然后枚举 j,判断能否从 j 转移到 i,判断方法就是每个盒子最多放 j 个小球,那么就是需要 \lfloor\frac{i}{j}\rfloor\times (i\bmod j)+\frac{\lfloor\frac{i}{j}\rfloor\times(\lfloor\frac{i}{j}\rfloor-1)}{2}\times j\le k 是否成立,并且需要满足 j\times m\ge i,暴力转移即可。

Subtask 3

注意到可以二分,复杂度 O(n\log n)

Subtask 4

注意到可以双指针,复杂度 O(n)

Subtask 5

## Subtask 6 我们不妨换个方向思考,设 $g_i$ 表示进行 $i$ 此操作,最多能处理的 $n$,每次 $g_i$ 由 $g_{i-1}$ 为基准,向上面一样二分答案得到。 注意到当 $n\le k$ 时,每次膨胀必然膨胀两倍以上(因为可以构造 $g_{i+1}=2g_i$,此时 $g_i$ 个放在 $0$ 号盒子,剩下的放在 $1$ 号盒子,答案一定不劣于这个,即 $g_{i+1}\ge2g_i$)。 然后复杂度就是 $O(\log^2 n)$,如果有较好的推式子能力或许可以 $O(\log n)$。 ## Subtask 7 留给复杂度比较神秘的人,或者挂分的。 ## Subtask 8 在当 $g_i\ge k$ 时,我们发现有 $g_{i+1}=g_i+k$,简单数学,可以让这部分做到 $O(1)$,最终复杂度为 $O(T\log^2 n)$,最快能做到 $O(T\log n)$,如果你比这个还快,欢迎私信。 ```cpp #include<bits/stdc++.h> #define int __int128 using namespace std; void _main(){ long long c1,c2,c3; int n,m,k; cin>>c1>>c2>>c3; n=c1,m=c2,k=c3; if(n==1){ cout<<0<<endl; return; } if(m==1){ cout<<-1<<endl; return; } int p; p=1; int ans; ans=0; while(p<n&&p<k){ ans++; int l,r; l=p,r=min(p*m,n); while(l<r){ int mid; mid=l+r+1; mid>>=1; int z; z=mid/p; int g; g=mid%p; if(z*g+z*(z-1)/2*p<=k){ l=mid; }else{ r=mid-1; } } p=l; } if(p>=n){ cout<<(long long)ans<<endl; return; } ans+=(n-p+k-1)/k; cout<<(long long)ans<<endl; } signed main(){ long long t; cin>>t; while(t--){ _main(); } return 0; } ```