FZU2018级算法第二次作业 2.10 逆序数(权值线段树)

题目:

  Nk 最近喜欢上了研究逆序数,给出一个由 1…n 组成的数列 a1,a2,a3…an,

  a1的逆序数就是在 a2…an 中,比 a1 小的数的数量,而 a2 的逆序数就是 a3….an 中比 a2 小的数的数量,以此类推。

  例如,数列 5,3,4,2,1 的逆序数序列就是 4,2,2,1,0.

  那么,如果给出一个数列的逆序数序列,你能不能还原得到他的原数列?

  ★数据输入

  每个测试数据是一个正整数 n。代表数列长度(1<=n<=500),并且原数列中的值的范围是[1,n]。

  然后输入 n 个正整数 ai(0<=ai<n)

  ★数据输出

  输出原始数列,两个数字间中间用空格隔开。

输入示例 输出示例
5
4 2 2 1 0
5 3 4 2 1

 

  分析下问题,不难发现,第一个数字填入时,除了自己以外的所有数字都在它的后面,因此第一个数字为4。我们可以将1-n所有数字作为一个集合,当第i个数后有ai个比他小的数时,集合中第ai+1大的数就是第i个数字,随后将这个数字从集合中删除。由于n数值过小(虽然线段树不交但是也不至于小到只有500),可以使用链表来完成,也可以使用set进行维护,复杂度均为$O(n^{2})$,当然,set维护写起来比较方便,建议用set维护。这里分享$O(nlogn)$做法,使用权值线段树。

先贴上set代码

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair <int,int> pii;
#define rep(i,x,y) for(int i=x;i<y;i++)
#define rept(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)
#define pb push_back
#define fi first
#define se second
#define mes(a,b) memset(a,b,sizeof a)
const int inf=0x3f3f3f3f;

set<int> s;
set<int> ::iterator pos;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin>>n;
    rept(i,1,n)
        s.insert(i);
    rept(i,1,n)
    {
        if(i-1) cout<<" ";
        int x;
        cin>>x;
        pos=s.begin(); 
        for(int i=0;i<x;i++,pos++);
        cout<<*pos;
        s.erase(pos);
    }
    cout<<"\n";
    return 0;
}

 

  对于1-n所有数,建成线段树,每个点标记第i个数为数组中第几大,对于线段树维护区间最大值,每输入一个ai,对于线段数找出大于ai的第一个点,即左区间有大于ai的找左区间,左区间没有则查询右区间。假设数组中第i个数为bi,将线段树中bi的值赋值为0,然后将$[bi+1,n]$区间的每个元素减1即可。

 

贴上线段树AC代码

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair <int,int> pii;
#define rep(i,x,y) for(int i=x;i<y;i++)
#define rept(i,x,y) for(int i=x;i<=y;i++)
#define pb push_back
#define fi first
#define se second
#define mes(a,b) memset(a,b,sizeof a)

const int maxn=505;
ll arrcy[100005];
class Tree//线段树 
{
    public:
        int l,r;
        ll plus,val;
        Tree()
        {
            l=r=val=plus=0;
        }
}tree[maxn<<2];

void build(int id,int l,int r);
void add(int id,int l,int r,ll num);
void push_down(int id);
int find(int id,int num);
int main()
{
    int n,x,ans;
    cin>>n;
    build(1,1,n);
    rep(i,0,n)
    {
        cin>>x;
        ans=find(1,x);
        add(1,ans+1,n,-1);
        add(1,ans,ans,-n);
        cout<<ans<<" ";
    }
    return 0;
}

void build(int id,int l,int r)
{
    tree[id].l=l;
    tree[id].r=r;
    if(l==r)
    {
        tree[id].val=l;
        return ;
    }
    int mid=(l+r)/2;
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
    tree[id].val=max(tree[id*2].val,tree[id*2+1].val);
}
void add(int id,int l,int r,ll num)
{
    if(tree[id].l>=l&&tree[id].r<=r)
    {
        tree[id].plus+=num;
        tree[id].val+=num;
        return ;
    }
    if(tree[id].l>r||tree[id].r<l) return ;
    push_down(id);
    if(tree[id*2].r>=l) add(id*2,l,r,num);
    if(tree[id*2+1].l<=r) add(id*2+1,l,r,num);
    tree[id].val=max(tree[id*2].val,tree[id*2+1].val);    
}
void push_down(int id)
{
    rept(j,id*2,id*2+1)
    {
        tree[j].val+=tree[id].plus;
        tree[j].plus+=tree[id].plus; 
    }
    tree[id].plus=0;
}
int find(int id,int num)
{
    if(tree[id].l==tree[id].r) return tree[id].l;
    push_down(id);
    if(tree[id*2].val>num) return find(id*2,num);
    else return find(id*2+1,num);
}

 

posted @ 2019-09-23 00:21  GGMU  阅读(174)  评论(0编辑  收藏  举报