Loading

Codeforces1603 C.Extreme Extension DP 根号性质

Codeforces1603 C.Extreme Extension DP 根号性质

题意

定义对区间\([l,r]\)的extreme values为

每次操作选择一个下标\(index\),将\(a_{index}\)拆分为\(x,y\)满足\(x + y = a_{index}\)

最后使得区间\([l,r]\)非递减的总操作次数

\([1,n]\)的每个子数组的extrme values的和

\[1 \leq n \leq 10^5\\ 1 \leq a_i \leq 10^5 \]

分析

首先解决只有一个询问\([1,n]\)的答案如何计算

对于\(a_i,a_{i+1}\),如果\(a_i < a_{i+1}\) 显然不做任何操作更优

否则应该在\(x_{max} < a_{i+1}\)的条件下使得\(x_{min}\) 取到最大值,不难发现这样的操作方法就是拆分为\(\lceil\frac{a_{i}}{a_{i+1}}\rceil\)个数,令最大的取\(\lfloor\frac{a_i}{\lceil\frac{a_{i}}{a_{i+1}}\rceil}\rfloor\)

于是如果只是求\([1,n]\)就可以从后往前扫一遍求出答案

现在考虑子问题,考虑\(dp_{i,j}\)表示第\(i\)个数作为开始,且拆分出的最小的数为\(j\)的方案

那么\(dp_{i-1,\lfloor\frac{a_i}{\lceil\frac{a_{i}}{j}\rceil}\rfloor}\) += \(dp_{i,j}\)

容易注意到这样\(i-1\)至多有\(\sqrt{a_i}\)个状态,所以这个状态转移实际上是\(O(\sqrt{a_i})\)

于是复杂度就是\(O(n\sqrt{a_i})\)

考虑\(dp\)的贡献就是让前\(i\)个数连接进来,于是对于状态\(i,j\)\(ans += dp_{i,j}*(cnt-1) * i\)

代码

#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define all(x) x.begin(),x.end()
 
using namespace std;
typedef long long ll;
typedef vector<int>VI;
inline ll rd(){
    ll x;
    scanf("%lld",&x);
    return x;
}
 
const int MOD = 998244353;
 
inline void add(int &a,int b){
    a += b;
    if(a >= MOD) a -= MOD;
}
 
inline int mul(int a,int b){
    return (ll)a * b % MOD;
}
 
inline int ksm(int a,int b = MOD - 2,int m = MOD){
    int ans = 1;
    int base = a;
    while(b){
        if(b & 1) ans = mul(ans,base);
        base = mul(base,base);
        b >>= 1;
    }
    return ans;
}

const int maxn = 1e5 +  5;

int dp[2][maxn];

void solve(){
    int n = rd();
    VI a(n + 1);
    VI v[2];
    for(int i = 1;i <= n;i++)
        a[i] = rd();
    int ans = 0;
    for(int i = n;i >= 1;i--){
        int k = i & 1;
        v[k].push_back(a[i]);
        dp[k][a[i]] = 1;
        int lst = a[i];
        for(auto it:v[k ^ 1]) {
            int y = dp[k ^ 1][it];
            int cnt = (a[i] + it - 1) / it;
            add(dp[k][a[i] / cnt],y);
            add(ans,mul(y,mul(i,cnt - 1)));
            if(lst != a[i] / cnt) {
                v[k].push_back(a[i] / cnt);
                lst = a[i] / cnt;
            }
        }
        for(auto it:v[k ^ 1] ) dp[k ^ 1][it] = 0;
        v[k ^ 1].clear();
    }
    for(auto it:v[0]) dp[0][it] = 0;
    for(auto it:v[1]) dp[1][it] = 0;
    printf("%d\n",ans);
}

int main(){
    int T = rd();
    while(T--)
        solve();
}
posted @ 2021-11-01 17:18  MQFLLY  阅读(132)  评论(0编辑  收藏  举报