题目链接: http://acm.oinsm.com/problem.php?cid=1027&pid=4
思路
今天周赛的一道题目,刚看到以为是一道单调栈的题目,后面写完,交了好几发,搓了一个样例,结果发现就错了!!这个8被丢掉了!所以还剩不到10分钟的时候立马转变了思路,用vector来存,从右往左,每次比较最后一个数如果比它小则加入vector,否则,就二分去找答案!
5
10 3 6 2 8
所以思路1: 单调序列 + 二分
思路2: 线段树:
题目: 对于每个数查找他右边的比他小的数且离他远的数,所有我们可以通过线段树来维护区间的最小值,对于这个数列,我们从右往左查询! 用INF初始化整个线段树(1-n),从右往左查询,每查询完一个数,就用单点修改把这个数加入到线段树中,然后继续往左.
对于每次查询,我们先右后左(因为我们是从右往左放入数),如果左右节点的值小于你要查询的值,则继续递归,直到到达叶节点返回即可!
代码
- 单调序列 + 二分
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
const long long INF = 0x3f3f3f3f;
typedef long long ll;
int num[MAXN];
int ans[MAXN];
int main()
{
ios::sync_with_stdio(false);
int n;
while(cin >> n) {
for(int i = 1; i <= n; i++) cin >> num[i];
vector<pair<int, int> > ve;
ve.clear();
for(int i = n; i >= 1; i--) {
if(ve.size() == 0 || num[i] <= ve.back().second) {
ve.emplace_back(i, num[i]);
ans[i] = -1;
}
else {
// cout << "**************" << endl;
// for(auto j : ve) {
// cout << j.second << " ";
// }
// cout << "**************" << endl;
int l = 0, r = ve.size() - 1;
ans[i] = -1;
while(l <= r) {
int mid = (l + r) / 2;
// cout << mid << endl;
if(ve[mid].second > num[i]) {
l = mid + 1;
}
else {
r = mid - 1;
ans[i] = max(ans[i], ve[mid].first - i - 1);
}
}
}
}
for(int i = 1; i <= n; i++) {
if(i != 1) cout << " ";
cout << ans[i];
}
cout << endl;
}
return 0;
}
- 线段树
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
const int INF = 1e9 + 7;
pair<int, int> node[MAXN<<2];
int num[MAXN];
//线段树模板(以区间和为例)
//更新数据
inline void PushUp(int root)
{
node[root] = (node[root<<1].first > node[root<<1|1].first) ? node[root<<1|1] : node[root<<1];
}
//1.建树
inline void BuildTree(int root, int l, int r) //[l,r]当前节点的区间, root 当前节点的编号
{
node[root].first = INF; //建树先左后右
node[root].second = -1;
if(l == r){
//说明当前已经到达叶子节点
node[root].first = INF; //建树先左后右
node[root].second = -1;
return ;
}
int mid = (l + r) >> 1;
//继续递归建树
BuildTree(root<<1, l, mid);//左
BuildTree(root<<1|1, mid + 1, r);//右
//其他操作(例如更新区间和)
PushUp(root);
}
//2.单点修改
inline void Update(int root, int l, int r, int index, pair<int, int> value)
{
// root 当前节点编号 [l,r] 树的区间范围, index 修改的节点编号, value 修改的值
if(l == r){
//到达当前节点
node[root] = value;
return ;
}
int mid = (l + r) >> 1;
if(index <= mid)
Update(root<<1, l, mid, index, value);
else
Update(root<<1|1, mid + 1, r, index, value);
//修改后记得更新
PushUp(root);
}
//4.区间查询
inline pair<int, int> Query(int root, int l, int r, int value)
{
// root 当前节点的编号, [l,r] 线段树区间, [L,R] 需要修改的区间范围
if(l == r){
//当前区间在查询区间内, 直接返回节点值
// cout << "------------" << endl;
// cout << root << " " << node[root].second << endl;
// cout << "------------" << endl;
return node[root];
}
int mid = (l + r) >> 1;
pair<int, int> a, b;
a.first = a.second = b.first = b.second = -1;
if(node[root<<1|1].first < value)
return Query(root<<1|1, mid + 1, r, value);
if(node[root<<1].first < value)
return Query(root<<1, l, mid, value);
return {-1, -1};
}
int ans[MAXN];
int main()
{
int n;
while(scanf("%d", &n) != EOF) {
for(int i = 1; i <= n; i++) {
scanf("%d", &num[i]);
}
BuildTree(1, 1, n);
for(int i = n; i >= 1; i--) {
pair<int, int> tmp = Query(1, 1, n, num[i]);
// cout << i << " " << tmp.first << " " << tmp.second << endl;
if(tmp.first == -1) {
ans[i] = -1;
}
else {
ans[i] = tmp.second - i - 1;
}
Update(1, 1, n, i, {num[i], i});
}
for(int i = 1; i <= n; i++) {
if(i != 1) printf(" ");
printf("%d", ans[i]);
}
printf("\n");
}
return 0;
}