CF1603C-Extreme Extension【整除分块,dp】
正題
題目鏈接:http://codeforces.com/contest/1603/problem/C
題目大意
定義一個序列aaa的f(a)f(a)f(a)為你每次可以將序列中的一個數zzz分裂成x+y=zx+y=zx+y=z,然后再把x,yx,yx,y放回原來的位置,然后f(a)f(a)f(a)表示把aaa變成不降序列的最少操作次數
給出一個長度為nnn的序列aaa,求它所有子區間的fff值的和。
1≤n,ai≤1051\leq n,a_i\leq 10^51≤n,ai?≤105
解題思路
顯然的每個數字aia_iai?最終肯定是分解為k?1k-1k?1個?aik?\lfloor\frac{a_i}{k}\rfloor?kai???和一個?aik?\lceil\frac{a_i}{k}\rceil?kai???。
然后我們對于一個序列可以從右往左每個選擇能分解的最小的次數來分肯定是最優的。
那么考慮暴力的dpdpdp,設fi,jf_{i,j}fi,j?表示現在第iii個,所有左端點為iii的區間中aia_iai?分解的最前面那個為kkk的方案,然后倒著轉移就好了。
考慮到jjj肯定是某個?aik?\lfloor\frac{a_i}{k}\rfloor?kai???,而?aik?\lfloor\frac{a_i}{k}\rfloor?kai???最多只有2ai2\sqrt a_i2a?i?種取值,所以我們可以只枚舉這些取值就好了。
時間復雜度:O(nai)O(n\sqrt a_i)O(na?i?),略微卡常
code
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll N=1e5+10,P=998244353; ll T,n,ans,a[N],f[2][N],g[2][N]; signed main() {scanf("%lld",&T);while(T--){scanf("%lld",&n);for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);ll o=0;g[o][a[n]]=1;ans=0;for(ll i=n-1;i>=1;i--){o^=1;ll m=a[i+1];for(ll l=1,r;l<=m;l=r+1){r=m/(m/l);ll x=m/l,w=(a[i]+x-1)/x,k=a[i]/w;f[o][k]+=f[!o][x]+(w-1)*g[!o][x];g[o][k]+=g[!o][x];ans+=f[!o][x];ans%=P;f[!o][x]=g[!o][x]=0;}g[o][a[i]]++;}for(ll l=1,r;l<=a[1];l=r+1)r=a[1]/(a[1]/l),(ans+=f[o][a[1]/l])%=P,f[o][a[1]/l]=g[o][a[1]/l]=0;printf("%lld\n",ans);}return 0; }總結
以上是生活随笔為你收集整理的CF1603C-Extreme Extension【整除分块,dp】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: P7920-[Kubic]Permuta
- 下一篇: win10电脑磁盘怎么分区以及合并分区电