题源
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;
}
算法分析
都在注释里了
代码重点分析(可无)
无