zoukankan      html  css  js  c++  java
  • 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}) 取到最大值,不难发现这样的操作方法就是拆分为(lceilfrac{a_{i}}{a_{i+1}} ceil)个数,令最大的取(lfloorfrac{a_i}{lceilfrac{a_{i}}{a_{i+1}} ceil} floor)

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

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

    那么(dp_{i-1,lfloorfrac{a_i}{lceilfrac{a_{i}}{j} ceil} floor}) += (dp_{i,j})

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

    于是复杂度就是(O(nsqrt{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
    ",ans);
    }
    
    int main(){
        int T = rd();
        while(T--)
            solve();
    }
    
  • 相关阅读:
    开源blog搬家工具
    嵌入式语言Lua
    ChangeMan Version Manager Workstation 8.1 command line quick check in/out
    Mssql合并列值(三个关系表合并)
    心得——学习中做笔记应该做到哪
    MFC Dialog 透明渐变
    【转】SSDT&Shadow Hook的实现,完整代码。可编译
    ssdt 表结构及hook的一般理解
    <转>ssdt hook 源码
    【转】_declspec(naked) 使用
  • 原文地址:https://www.cnblogs.com/hznumqf/p/15494678.html
Copyright © 2011-2022 走看看