P6198 、Can You Help ZSGW(洛谷月赛和武大校赛初赛题)

牛客题目链接
洛谷题目链接

写分治类型的题太少了,而且看到一篇感觉很绝妙的写法,故学习之后,自己总结记录一下这个题

题意:

有一个排列,已知我们对于这个排列执行单调栈算法过程中,遍历到每一 个位置之后单调栈的大小,有些位置可能缺失,输入为-1。求一个满足这种情况 的字典序最小的排列。

思路:

首先我们应该做的事,是补全这个单调栈数组 b。单调栈数组由于单调栈 算法的特点,必然满足这么几个特点:

  • b[1] = 1
  • 若 b[i] > b[i −1],则 b[i] = b[i −1] +1,且 a[i] > a[i −1]。
  • 对于 b[i] <= b[i −1],一定有 a[i] < a[i −1]。

我们从左到右依次补全每一个为 −1 的格子,那么策略应该是:

  • 若 i = 1,b[i] = 1。
  • 否则,如果我们填一个比 b[i −1] 小的数的话,在输出答案中, a[i −1] >a[i] ,在字典序上不会是个好主意。所以应该填 b[i −1] +1。
    补全数组之后,规律如下: 首先我们可以发现所有为 1 的位置组成了以最后一个 1 截止的降序序列。然后对 于每个被 1 分割的子区间,2 也会满足类似的规律。然后对于 2 进一步分 割的子区间 3 也会如此。

代码:

分治函数中,开始找出全部1的位置,按照降序依次填入数字,然后把vector为1的位置都删掉,然后对于每个分割的区间,使用2,3...。

#include <iostream>
#include<stdio.h>
#include<vector>
#include<string>
#include<stack>
#include<algorithm>
using namespace std;
#define auto vector<int>::reverse_iterator
const int N=1e5+10;
vector<int>v[N];
stack<int>s;
int n;
int cur=1;
int a[N];
int b[N];
//每次运行完,都能被保证v和s清空了

void fill_arr(){
    a[1]=1;
    for(int i=2;i<=n;i++){
        if(a[i]==-1)a[i]=a[i-1]+1;
    }
}
void dfs(int l,int r,int pos){
    if(l>r)return;
    auto ed =v[pos].rend();
    for(auto i=v[pos].rbegin();i!=ed;i++){
        if(*i<=r)s.push(*i);
        else break;
    }
    while(!s.empty()&&s.top()>=l)b[s.top()]=cur++,s.pop();
    int xx = v[pos].back();
    dfs(l,xx-1,pos+1);
    v[pos].pop_back();
    while(!v[pos].empty()&&v[pos].back()<=r){
        dfs(xx+1,v[pos].back()-1,pos+1);
        xx = v[pos].back();
        v[pos].pop_back();
    }
    dfs(xx+1,r,pos+1);
    
}
void gao(){
    cur=1;
    fill_arr();
    for(int i=n;i;i--){
        v[a[i]].push_back(i);
    }
    dfs(1,n,1);
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        gao();
        for(int i=1;i<=n;i++)printf("%d ",b[i]);
        puts("");
    }
}

猜你喜欢

转载自www.cnblogs.com/gzr2018/p/12702999.html