C - The Battle of Chibi

news2024/9/24 11:26:03

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

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

那么状态计算就为  dp[ i ][ j ]=\sum dp[k][j-1](a[k]<a[i],k<i)  ,

那我们如果不优化直接写,一层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上由这个解析

#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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/854636.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构刷题训练——链表篇(一)

目录 前言 题目一&#xff1a;链表的中间节点 思路 分析 题解 题目二&#xff1a;链表中倒数第k个结点 思路 分析 题解 题目三&#xff1a;合并两个有序链表 思路 分析 题解 方法二 题解 题目四&#xff1a;链表的回文结构 思路 分析 题解 总结 前言 今天我将开…

家政小程序开发制作

家政小程序是一种基于移动互联网的工具&#xff0c;旨在为用户提供方便快捷的家政服务。下面是家政小程序的功能介绍&#xff1a; 1. 家政服务展示&#xff1a;家政小程序可以展示各类家政服务的详细信息&#xff0c;包括清洁、保姆、月嫂、保洁等多种服务项目&#xff0c;以及…

【vue3】基础知识点-pinia

学习vue3&#xff0c;都会从基础知识点学起。了解setup函数&#xff0c;ref&#xff0c;recative&#xff0c;watch、computed、pinia等如何使用 今天说vue3组合式api&#xff0c;pinia 戳这里&#xff0c;跳转pinia中文文档 官网的基础示例中提供了三种写法 1、选择式api&a…

性能测试场景分析并设计?超细案例讲解

前言 性能测试场景&#xff0c;其实和功能测试没什么区别&#xff0c;只是侧重点不同。 我们在功能测试中经常用到的等价类边界值等分析和设计测试case的方法&#xff0c;目的是为了尽可能的覆盖业务场景&#xff0c;避免遗漏导致的功能逻辑缺失或者未达到预期。 而在性能测试…

【C++】初识模板

C模板入门 一、泛型编程 二、函数模板1. 函数模板的概念2. 函数模板格式3. 函数模板的原理4. 函数模板的实例化5. 模板参数的匹配原则 三、类模板 一、泛型编程 假设我们想实现一个交换函数&#xff0c;并且支持不同类型的参数实现&#xff0c;我们可以用 typedef 将类型进行重…

V2MOM工作法

V2MOM分别代表愿景&#xff08;vision&#xff09;、价值&#xff08;values&#xff09;、方法&#xff08;methods&#xff09;、障碍&#xff08;obstacles&#xff09;、衡量指标&#xff08;measurement&#xff09;。 第一&#xff0c;我真正想要的是什么呢&#xff1f;这…

ubuntu磁盘管理

show partition information 挂载设备在这 显示文件系统信息 build file system mkfs -t ext4 /dev/nvme0n1p4命令作用&#xff1a;将/dev/nvme0n1p4 格式化为 ext4 建立交换分区 mkswap -c -v1 /dev/nvme0n1p4 102400-c&#xff1a;check -v1&#xff1a;新版交换分区 -v0&…

四、web应用程序技术——HTTP

文章目录 1 HTTP请求2 HTTP响应3 HTTP方法4 URL5 HTTP消息头5.1 常用消息头5.2 请求消息头5.3 响应消息头 6 cookie7 状态码8 HTTP代理9 HTTP身份验证 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是访问万维网使用的核心通信协议&…

Linux 1.2.13 -- IP分片重组源码分析

Linux 1.2.13 -- IP分片重组源码分析 引言为什么需要分片传输层是否存在分段操作IP分片重组源码分析ip_createip_findip_frag_createip_doneip_glueip_freeip_expireip_defragip_rcv 总结 本文源码解析参考: 深入理解TCP/IP协议的实现之ip分片重组 – 基于linux1.2.13 计网理论…

2023Android面试,如果想卷请继续。备战金九银十

随着移动互联网的快速发展&#xff0c;Android开发岗位竞争也越来越激烈。作为一名Android程序员&#xff0c;面试是进入理想公司的重要一步。本文将分析市场对Android开发岗位的需求&#xff0c;分析2022年的Android开发岗位面试情况&#xff0c;并总结出历年来常见的面试题目…

MySQL中同比和环比语句如何写?

营收表如下&#xff08;表名&#xff1a;a&#xff09;如下图&#xff1a; 营收表 year month money 2021 1 1000 2021 2 1200 2022 1 1300 2022 2 1500 需要算出2022年营收同比与环比&#xff1a; 同比&#xff1a;和去年同月相比&#xff08;1300-1000/1000*100%&#xff0…

Python类的设计

Python类的设计 # 定义一个闹钟类 class Clock:__cureen_keyNone # 私有成员不能改变和使用def __init__(self, id, price): # 类对象是立即自动执行self.id idself.price pricedef ring(self):import winsound # 内置声音方法winsound.Beep(2000,3000)clock1 Clock(…

自然语言处理学习笔记(六)————字典树

目录 1.字典树 &#xff08;1&#xff09;为什么引入字典树 &#xff08;2&#xff09;字典树定义 &#xff08;3&#xff09;字典树的节点实现 &#xff08;4&#xff09;字典树的增删改查 DFA&#xff08;确定有穷自动机&#xff09; &#xff08;5&#xff09;优化 1.…

Python基础--序列操作/函数

Python基础 1.序列的操作 2.函数 1. 数据类型的具体操作 1.1 序列操作--列表具体操作&#xff1a; #定义列表 listA [] #定义一个空列表 listB [1,2.8,"你好",listA,[1,2,3]] # 访问列表 print(listB)#查看整个列表 print(listB[2])#查看单个…

docker 安装mongodb 虚拟机安装mongodb

生产环境直接安装比较好&#xff0c;以及使用集群环境&#xff0c;本文仅测试交流使用&#xff0c;我用来写分布式im测试使用&#xff1a; nami-im: 分布式im, 集群 zookeeper netty kafka nacos rpc主要为gate&#xff08;长连接服务&#xff09; logic &#xff08;业务&…

MySQL:内置函数、复合查询和内外连接

内置函数 select 函数; 日期函数 字符串函数 数学函数 其它函数 复合查询&#xff08;多表查询&#xff09; 实际开发中往往数据来自不同的表&#xff0c;所以需要多表查询。本节我们用一个简单的公司管理系统&#xff0c;有三张 表EMP,DEPT,SALGRADE来演示如何进行多表查询…

设计模式行为型——访问者模式

目录 访问者模式的定义 访问者模式的实现 访问者模式角色 访问者模式类图 访问者模式举例 访问者模式代码实现 访问者模式的特点 优点 缺点 使用场景 注意事项 实际应用 访问者模式的定义 访问者模式&#xff08;Visitor Pattern&#xff09;属于行为型设计模式&am…

总结950

7:00起床 7:30~8:00复习单词300个&#xff0c;记忆100个 8:10~9:30数学660&#xff0c;只做了10道题&#xff0c;发现对各知识点的掌握程度不一。有些熟练&#xff0c;有些生疏 9:33~10:25计算机网络课程1h 10:32~12:02继续660&#xff0c;也不知道做了几道 2:32~4:00数据…

Node.js |(四)HTTP协议 | 尚硅谷2023版Node.js零基础视频教程

学习视频&#xff1a;尚硅谷2023版Node.js零基础视频教程&#xff0c;nodejs新手到高手 文章目录 &#x1f4da;HTTP概念&#x1f4da;窥探HTTP报文&#x1f4da;请求报文的组成&#x1f407;HTTP请求行&#x1f407;HTTP请求头&#x1f407;HTTP的请求体 &#x1f4da;响应报文…

阔别三年,领先回归!别克LPGA锦标赛申城十月再启高球盛会

2023年8月4日——2023年金秋十月&#xff0c;阔别中国赛场已久的别克LPGA锦标赛将强势归来&#xff0c;于10月12日至15日在上海旗忠花园高尔夫俱乐部再次拉开帷幕。作为三年来首个回归、同时也是今年国内唯一开赛的国际顶级高尔夫职业赛事&#xff0c;别克LPGA锦标赛将吸引全世…