DP(用于寻找满足条件的子区间个数)

题源
codeforce round 959 C# 题目

题目分析

雅罗斯拉夫正在玩一款电脑游戏,在其中一个关卡中,他遇到了n个排成一排的蘑菇。每种蘑菇都有自己的毒性水平;从一开始,第 i 个蘑菇的毒性级别为 a_i。雅罗斯拉夫
可以选择两个整数1<l<r<n,然后他的角色会从左到右轮流一个一个地吃这个子段中的蘑菇,即编号为l,1+1,1+2的蘑菇, …,河。

该角色的毒性级别为 g,最初等于 0。计算机游戏由数字 x 任何给定时间的最大毒性级别定义。当食用毒性等级为 k 的蘑菇时,会发生以下情况:

1.角色的毒性等级增加k。
2.如果是g<=x,则继续处理;否则,g 变为零并且该过程继续。
雅罗斯拉夫开始感兴趣的是有多少种方法可以选择 I 和 r 的值,使得 g 的最终值不为零。帮助雅罗斯拉夫找到这个号码!

解答与分析

难点分析

1.毒性一旦高于x就会归零,也就代表着不同的起点意味不同的长度,情况比较多
2.他让找的是区间的个数一共有多少满足情况的区间数目,这个当初不知道从何下手。

解决

难点一决定了此题可以用DP解决,寻找的区间可以传递,从i到j如果小于x则可以满足条件,如果从i到j区间毒性刚好开始大于x,那么( i ,j )该区间毒性为0,可以与( j , k )毒性小于等于x的区间合并,是为传递性。
难点二,由于区间的左右边界都不确定,因此我们可以利用解决第一条的思路,进行固定一个边界,搜索另一个边界的操作,同时在记录上运用DP/

补充
官方解答,思路一致

完整代码

前期

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define For for (ll i = 1; i <= n; i++)
#define rFor for (ll i = n; i > 0; i--)
#define rep(i, sta, end) for (ll i = sta; i <= end; i++)
#define rrep(i, end, sta) for (ll i = end; i >= sta; i--)
#define All(x) for (auto item : x)

inline void solve(){
int n,x;
	cin>>n>>x;
	vector<ll > arr(n+10),dp(n+10,0);
	For cin>>arr[i];
	partial_sum(arr.begin()+1,arr.begin()+1+n,arr.begin()+1);
	rrep(i,n-1,0){
		int len = upper_bound(arr.begin()+1,arr.begin()+1+n,arr[i]+x)- arr.begin();
		//总结一下,如果数组下标是1到n,二分搜索函数搜到的下标减去begin()得到的值len正好是目标值下标
		//例如,1 2 3 4 5 6 7 ,前面空出来一个下标为0的位置,每一个值与下标相同,
		//则二分搜索比5大的最近一个值,下标指向6,则 6的下标 - begin()= 6,就是6的下标值
		dp[i]=dp[len] + len - i - 1; 
		//以i为左边界,从i 到len这个区间内部(不包括边界)上的每一个点都可以作为右边界,共len-i-1个
		//可以看出来dp[i]记录的是以i为左边界,满足题目要求的区间的个数
 	}
	cout<<accumulate(dp.begin()+1,dp.begin()+1+n,0LL)<<endl;
}

int main() {
	std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
	int num=1;
	cin>>num;
	while(num--)
		solve();
	
	return 0;
}  

算法分析

都在注释里了

代码重点分析(可无)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值