首页 > 其他分享 >[ABC017D] サプリメント 题解

[ABC017D] サプリメント 题解

时间:2023-12-02 20:13:01浏览次数:40  
标签:int 题解 sum dp ABC017D 复杂度 DP mod

题目传送门~

一道 DP 前缀和优化好题。

题目分析

首先,朴素 DP 非常好想。可以从后向前考虑,设 \(f_i\) 表示从第 \(i\) 个补品开始的摄取方法数。

摄取一个补品:\(f_i = f_{i+1}\)

摄取两个补品:\(f_i = f_{i+2}\)

以此类推。

则转移方程为:

\[f_i = f_{i+1} + f_{i+2} + \dots + f_{j} \]

这里的 \(j\) 指 \(i\) 向后最远能取到的保证不重复的数,这一步可以用 unordered_map 判重。

朴素 DP 参考代码

#include <bits/stdc++.h>
using namespace std;
const int N = 100005,mod = 1e9+7;
int n,m,a[N],dp[N];
int main()
{
	cin >> n >> m;
	for (int i = 1;i <= n;i++) cin >> a[i];
	dp[n+1] = 1;
	for (int i = n;i >= 1;i--)
	{
		dp[i] = dp[i+1];
		unordered_map<int,bool> mp;//map判重
		mp[a[i]] = 1;
		for (int j = i+1;j <= n;j++)
		{
			if (mp[a[j]]) break;
			dp[i] += dp[j+1] % mod,dp[i] %= mod;
			mp[a[j]] = 1;
		}
	}
	cout << dp[1] << endl;
	return 0;
} 

DP 优化

朴素 DP 时间复杂度 \(O(n^2)\),于是考虑优化。

朴素 DP 是 \(O(n)\) 枚举查找 \(j\) 的位置,那么可以预处理每个 \(i\) 对应 \(j\) 的位置,再通过前缀和求出 \(f_{i+1} + f_{i+2} + \dots + f_{j}\) 的值,就可以优化状态转移时间复杂度为 \(O(1)\)。

最终复杂度为 \(O(n)\)。

预处理的方法

怎么求出每个 \(i\) 对应的 \(j\) 的位置呢?我们发现 \(j\) 的位置是随着 \(i\) 单调递增的,因为 \(i-1\) 对应的 \(j\) 对于 \(i\) 来说肯定也是可行的。

所以很容易能想到使用双指针算法,时间复杂度 \(O(n)\)。

具体实现见代码。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 100005,mod = 1e9+7;
int n,m,a[N];
int dp[N],bk[N],sum[N];
unordered_map<int,int > mp;	
int main()
{
	cin >> n >> m;
	for (int i = 1;i <= n;i++) cin >> a[i];
	for (int i = 1,j = i;i <= n;i++) //双指针算法
	{
		while (j <= n && !mp[a[j]]) mp[a[j]]++,j++;
		bk[i] = j-1; //存储每个i最远能走到的数
		mp[a[i]]--;
	} 
	dp[n+1] = sum[n+1] = 1;
	for (int i = n;i >= 1;i--)
	{
		//dp[i] = dp[i+1] + dp[i+2] + ... + dp[bk[i] + 1]
		dp[i] =( sum[i+1]-sum[bk[i] + 2] + mod) % mod;
		sum[i] = (sum[i+1] + dp[i]) % mod;
	}
	cout << dp[1] << endl;
	return 0;
} 

标签:int,题解,sum,dp,ABC017D,复杂度,DP,mod
From: https://www.cnblogs.com/codwarm/p/17872147.html

相关文章

  • P4143 采集矿石 题解
    题目传送门给出字符串\(s\),以及数组\(a_1\sima_{|s|}\)。定义一个子串的排名为:字典序比它大的本质不同的子串个数\(+1\)。定义一个子串\(s[l,r]\)的权值为\(\sum\limits_{i=l}^ra_i\)。求有多少个子串的排名等于权值。\(|s|\le10^5,0\lea_i\le1000\)。......
  • java: 未报告的异常错误java.io.UnsupportedEncodingException; 必须对其进行捕获或声
    原问题代码:/**MD5编码相关的类@authorwangjingtao*/publicclassMD5{//首先初始化一个字符数组,用来存放每个16进制字符privatestaticfinalchar[]hexDigits={'0','1','2','3','4','5','6','7'......
  • CF1790F题解
    思路令$dis_i$为离$i$最近的黑点距离,$ans$是距离最近的一对黑点距离,我们可以发现,每次$i\getsi+1$后$ans$的更新只会与$dis_{c_i}$有关,因为$c_i$是新的黑点,然后再从$c_i$来一次BFS更新$dis_i$即可。更详细的在注释。代码#include<bits/stdc++.h>......
  • [题解]AT_abc224_e [ABC224E] Integers on Grid
    比较符合CCF造数据水平的题。思路首先可以用两个vector<pair<int,int>>v[N]分别将每一行、每一列的元素的权值与编号存储下来。那么可以对所有的\(v_i\)按照权值从小到大排序。那么发现对于所有的满足v[i][p].fst<v[i][q].fst的\((p,q)\)都可以建一条从\(p\)指......
  • CSP第31次认证题解 2023.9
    A、坐标变换(其一)样例输入3210100010-201-100样例输出21-1120-10题解按照题目,一个循环即可#include<bits/stdc++.h>usingnamespacestd;#defineN200010#definelllonglongtemplate<classT>inlinevoidread(T&a){Tx=0,s=1;......
  • 使用Navicat For MSSQL连接绿色版SQLServer2008R2问题解决
    问题1、创建连接时出现错误:[IM002][Microsoft][ODBC驱动程序管理器]未发现数据源名称并且未指定默认驱动程序(0)Navicat来连接SQLserver,这里确实有点麻烦,出现错误[IM002][Microsoft][ODBC驱动程序管理器]未发现数据源名称并且未指定默认驱动程序(0),解决方法:进入Navicat的安装......
  • CF1368题解
    CF1368CodeforcesGlobalRound8ABC略。CF1368DlinkCF1368D题意给定\(n\)个非负整数\(a_1,\cdots,a_n\)。你可以进行如下操作:选择两个不同的下标\(i,j\)满足\(1\leqi,j\leqn\),并将\(a_i\getsa_i\\mathsf{AND}\a_j,\a_j\getsa_i\\mathsf{OR}\a_j\),两个赋值......
  • newstarctf2023 reverse 题解汇总
    newstarctf2023reverse题解汇总week1easy_REdie查无壳64直接IDA启动跟到main函数找到两部分flag拼起来就行了。flag{we1c0me_to_rev3rse!!}ELFdie查64ELFIDA启动稍微读一下写个py逆一下它的加密就行了flag{D0_4ou_7now_wha7_ELF_1s?}importbase64a="VlxRV......
  • ISCTFpwn的ezpie题解
    目前接触的随机好像都与地址有关,而且还有一个特性也就是只是基址随机,只要有任意一个地址就可以知道其他所有具体地址。(libc和pie保护)这里将通过ezpie这道题介绍绕过pie的一种方式,泄露地址一获取全部地址的方法。本人还不太懂partiallywrite的原理,就不误人子弟了。这里我们看到v5......
  • 【POJ 1144】Network 题解(Tarjan算法求无向图的割点)
    一家电话线公司(TLC)正在建立一个新的电话电缆网络。它们连接由1到N的整数编号的几个位置。没有两个地方的数字相同。这些线路是双向的,总是连接在两个地方,在每一个地方,线路都以电话交换机结束。每个地方都有一个电话交换机。从每个地方可以通过其他地方的线路到达,但不需要直接连接,可......