C - The Battle of Chibi

这篇具有很好参考价值的文章主要介绍了C - The Battle of Chibi。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

题意:就是问你数组中长度为m的上升子序列(没说连续)有多少个。

1:可以想到状态表示dp[ i ][ j ] 代表以 a[i] 为结尾的且长度为 j 的严格单增子序列的数目,

那么状态计算就为    ,

那我们如果不优化直接写,一层n,一层 j 一层 k ,肯定会超时

2:考虑进行优化:
① 既然要优化求前缀和的速度,不妨对 dp[1∼n][1] 构造一个树状数组,对 dp[1∼n][2] 构造一个树状数组,⋯,对 dp[1∼n][m] 构造一个树状数组。这样一来,我要求 dp[i][j]。就可以再dp[1~n][j-1]z这颗树状数组里求前缀和了,这样一个前缀和就可以在 O(logn) 时间内完成。总时间复杂度变为 O(n2logn)。

②再想我们用树状数组数组时要以a[i]大小为下标,故要离散化一下

#include <bits/stdc++.h>
using namespace std;
#define pi acos(-1)
#define xx first
#define yy second
#define endl "\n"
#define lowbit(x) x & (-x)
#define int long long
#define ull unsigned long long
#define pb push_back
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define Ysanqian ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int N = 5e5 + 10, M = 1010, inf = 0x3f3f3f3f, mod = 1e9 + 7, P = 13331;
const double eps = 1e-8;
int n, m;
int dp[M][M];
int a[N], l[N];
int cases;
//dp[i][j]=sum(dp[k][j-1])(k<i,a[k]<a[i],这个由add维护)
void add(int x, int y, int c)//相当于开了m个树状数组,每个数组数组维护一个长度的前缀和即dp[1~n][1],dp[1~n][2]....dp[1~n][m]
{
    for (int i = x; i <= n; i += lowbit(i))
        dp[i][y] = (dp[i][y] + c) % mod;
}
int query(int x, int y)
{
    int res = 0;
    for (int i = x; i; i -= lowbit(i))
        res = (res + dp[i][y]) % mod;
    return res;
}
void Unique()
{
    vector<int> s;
    for (int i = 1; i <= n; i++)
        s.pb(a[i]);
    sort(s.begin(), s.end());
    s.erase(unique(s.begin(), s.end()), s.end());
    for (int i = 1; i <= n; i++)
    {
        int x = lower_bound(s.begin(), s.end(), a[i]) - s.begin() + 1;
        l[i] = x;
    }
}
void solve()
{
    memset(dp, 0, sizeof dp);
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    Unique();
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (j == 1)
                add(l[i], 1, 1);
            else
                add(l[i], j, query(l[i] - 1, j - 1));
        }
    }
    cout << "Case #" << ++cases << ":" << query(n, m) << endl;
}
signed main()
{
    Ysanqian;
    int T;
    // T = 1;
    cin >> T;
    while (T--)
        solve();
    return 0;
}

还有另一种写法,开一层树状数组即可acwing上由这个解析文章来源地址https://www.toymoban.com/news/detail-636229.html

#include <bits/stdc++.h>
using namespace std;
#define pi acos(-1)
#define xx first
#define yy second
#define endl "\n"
#define lowbit(x) x & (-x)
#define int long long
#define ull unsigned long long
#define pb push_back
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
// cout << "Case #" << ++cases << ": ";
#define Ysanqian ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int N = 1e6 + 10, M = 1010, inf = 0x3f3f3f3f, mod = 1e9 + 7, P = 13331;
const double eps = 1e-8;
int n, m;
int f[M][M];//表示以a[i]为结尾,长度为j的上升子序列的数量
int a[N], l[N], tr[N];
int cases;
void add(int x, int c)
{
    for (int i = x; i <= n; i += lowbit(i))
        tr[i] = (tr[i] + c) % mod;
}
int query(int x)
{
    int res = 0;
    for (int i = x; i; i -= lowbit(i))
        res = (res + tr[i]) % mod;
    return res;
}
void Unique() // 离散化
{
    vector<int> s;
    for (int i = 1; i <= n; i++)
        s.pb(a[i]);
    sort(s.begin(), s.end());
    s.erase(unique(s.begin(), s.end()), s.end());
    for (int i = 1; i <= n; i++)
    {
        int x = lower_bound(s.begin(), s.end(), a[i]) - s.begin() + 1;
        l[i] = x;
    }
}
void solve()
{
    cout << "Case #" << ++cases << ": ";
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    Unique();
    for (int i = 1; i <= n; i++) // 初始化第一层
        f[i][1] = 1;

    for (int j = 2; j <= m; j++) // 第一层已经算完了,我们改变循环的顺序,就可以只保留一棵树即可,为啥这样循环可以只开一棵树
    /*就在这做个解释吧,假设a[4]={20,30,40,10};离散化后大小为l[4]={2,3,4,1},
    f[1][1]=1;
    f[2][1]=1;初始化;   然后j=2这一层来说,首先清空tr数组,开始循环   
    f[3][1]=1;          i=1; f[1][2]可以由谁来转移呢,显然没有那就是0
    f[4][1]=1;          然后我们在l[1]这个位置上加上f[1][1],为下面得第二轮循环做准备
                        i=2; f[2][2]可以由谁来转移呢,显然没有那就是f[1][1]的数量,前提a[1]<a[2],故我们查询小于l[2]的数,刚好我们上一轮
                        l[1]加上了f[1][1],且l[2]>l[1],得以转移, 然后我们在l[2]这个位置上加上f[2][1],为下面得第三轮循环做准备
                        i=3;f[3][2]可以由谁来转移呢,显然没有那就是f[2][1]加上f[1][1]的数量,恰好我们前两轮都在相应得位置加上了,那么我们只需
                        询问l[1],l[2]是否小于l[3]就可以了,如果小于直接树状数组前缀和就把f[2][1]和f[1][1]加上了,
                        然后我们在l[3]这个位置上加上f[3][1],为下面得第三轮循环做准备
                        依次类推,可以看出我们的状态转移只会用到上一层,而我们这样先枚举j,正好解决了开m颗树的问题
    */
    {
        for (int i = 1; i <= n; i++)
            tr[i] = 0;
        for (int i = 1; i <= n; i++)
        {
                f[i][j] = query(l[i] - 1); // 查询小于l[i]的个数(我们把a[i]的值离散化为l[i]了,但是a[i]之间的大小关系并未发生改变)
            add(l[i], f[i][j - 1]);    // f[i][j]表示以a[i]结尾,长度为j的递增序列数量
        }
    }
    int res = 0;
    for (int i = 1; i <= n; i++)
        res = (res + f[i][m]) % mod;
    cout << res << endl;
}
signed main()
{
    Ysanqian;
    int T;
    // T = 1;
    cin >> T;
    while (T--)
        solve();
    return 0;
}

到了这里,关于C - The Battle of Chibi的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 动态规划算法解决背包问题,算法分析与C语言代码实现,时间效率解析

    🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨,经典算法的解析✨都在这儿,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏 🪔本系列专栏 -  数据结构与算法_勾栏听曲_0 🍻欢迎大家  🏹  点赞👍  评论📨  收藏⭐️ 📌个人主

    2023年04月16日
    浏览(50)
  • 贪心算法解决背包问题和动态规划解决0-1背包问题(c语言)

    运行结果如下: 运行结果如下: 总结: 贪心算法: 每一步都做出当时看起来最佳的选择,也就是说,它总是做出局部最优的选择。 贪心算法的设计步骤: 对其作出一个选择后,只剩下一个子问题需要求解。 证明做出贪心选择后,原问题总是存在最优解,即贪心选择总是安

    2024年02月04日
    浏览(56)
  • 最大子段和——用蛮力算法,分治策略,动态规划算法三种求法(C语言)

    目录 一、题目 二、算法求解 1、蛮力算法 伪代码  算法分析 程序 2、分治策略 伪代码 算法分析 程序 3、动态规划算法 伪代码 算法分析 程序 设A=a1,a2,...,an是n个整数的序列,称ai,....,aj为该序列的连续子序列,其中1=i=j=n,子序列的元素之和称为A的子段和: 例如,A=-2,11,-4,1

    2024年01月24日
    浏览(55)
  • LeetCode646. Maximum Length of Pair Chain——动态规划

    You are given an array of n pairs pairs where pairs[i] = [lefti, righti] and lefti righti. A pair p2 = [c, d] follows a pair p1 = [a, b] if b c. A chain of pairs can be formed in this fashion. Return the length longest chain which can be formed. You do not need to use up all the given intervals. You can select pairs in any order. Example 1: Input: pairs = [[

    2024年02月22日
    浏览(48)
  • 【LeetCode 算法】Find the Losers of the Circular Game 找出转圈游戏输家

    n 个朋友在玩游戏。这些朋友坐成一个圈,按 顺时针方向 从 1 到 n 编号。从第 i 个朋友的位置开始顺时针移动 1 步会到达第 (i + 1) 个朋友的位置( 1 = i n ),而从第 n 个朋友的位置开始顺时针移动 1 步会回到第 1 个朋友的位置。 游戏规则如下: 第 1 个朋友接球。 接着,第

    2024年02月12日
    浏览(53)
  • Codeforces Round 935 (Div. 3) - D. Seraphim the Owl (动态规划)

    大家排成 n 人的队列,从 i=1 号开始,向猫头鹰谢拉菲姆请教生命的意义。不幸的是,基里尔当时正忙着为这问题编写传说,所以他来得晚了一些,站在了 n 这个人之后,排在了队伍的最后。基里尔对这种情况完全不满意,于是他决定贿赂一些排在他前面的人。 对于队列中的

    2024年04月17日
    浏览(55)
  • 【算法】动态规划 ⑧ ( 动态规划特点 )

    求解类型 : 动态规划 必须是求 最值 , 可行性 , 方案数 , 三者之一 , 如果求其它内容 , 则不能使用动态规划算法 ; 求最值 : 最大值 , 最小值 等 ; 大规模问题的结果 由 小规模问题 的计算结果 取最大值 大规模问题的结果 由 小规模问题 的计算结果 取最小值 可行性 : 是否可行

    2023年04月08日
    浏览(48)
  • 【算法 - 动态规划】原来写出动态规划如此简单!

    从本篇开始,我们就正式开始进入 动态规划 系列文章的学习。 本文先来练习两道通过 建立缓存表 优化解题过程的题目,对如何将 递归函数 修改成 动态规划 的流程有个基本的熟悉。 用最简单的想法完成题目要求的 递归 函数; 定义明确 递归函数 的功能!!! 分析是否存

    2024年02月21日
    浏览(48)
  • 60题学会动态规划系列:动态规划算法第三讲

    简单多状态问题 文章目录 一.按摩师 二.打家劫舍系列 三.删除并获得点数 四.粉刷房子 力扣链接:力扣 一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,

    2024年02月08日
    浏览(49)
  • 【算法】动态规划 ① ( 动态规划简介 | 自底向上的动态规划示例 | 自顶向下的动态规划示例 )

    动态规划 , 英文名称 Dynamic Programming , 简称 DP , 不是具体的某种算法 , 是一种算法思想 ; 具体的算法都有具体的步骤 , 如 : 二分法 , 其 有固定的解决步骤 , 先取一个中心点 , 判断解在左边还是右边 , 然后在一边再取一个中心点 , 再进行判定 , 该算法有具体的步骤 ; 动态规划

    2024年01月16日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包