hdu 5592 线段树

来源:互联网 发布:薛之谦人品知乎 编辑:程序博客网 时间:2024/06/01 08:45
问题描述
ZYBZYBZYB有一个排列PPP,但他只记得PPP中每个前缀区间的逆序对数,现在他要求你还原这个排列.(i,j)(i<j)(i,j)(i < j)(i,j)(i<j)被称为一对逆序对当且仅当Ai>AjA_i>A_jAi>Aj
输入描述
第一行一个整数TTT表示数据组数。接下来每组数据:第一行一个正整数NNN,描述排列的长度.第二行NNN个正整数AiA_iAi,描述前缀区间[1,i][1,i][1,i]的逆序对数.数据保证合法.1≤T≤51 \leq T \leq 51T5,1≤N≤500001 \leq N \leq 500001N50000
输出描述
TTT行每行NNN个整数表示答案的排列.
输入样例
130 1 2
输出样例
3 1 2
题解:设fi是第i个前缀的逆序对数,pi是第i个位置上的数,则f​i​​−f​i−1​​是iii前面比pi大的数的个数.我们考虑倒着做,当我们处理完i后面的数,第i个数就是剩下的数中第f_i-f_{i-1}+1大的数,用线段树和树状数组可以轻松地求出当前第k个是1的位置,复杂度O(NlogN)

#include<cstdio>#include<algorithm>#include<string>#include<iostream>using namespace std;#define lson rt<<1,l,m#define rson rt<<1|1,m+1,rconst int maxn=50009;int a[maxn<<2],ans[maxn<<2];int sum[maxn<<2];void pushup(int rt){    sum[rt]=sum[rt<<1]+sum[rt<<1|1];}void build(int rt,int l,int r){    if(l==r){        sum[rt]=1;        return;    }    int m=(l+r)>>1;    build(lson);    build(rson);    pushup(rt);}int querry(int rt,int l,int r,int v){    if(l==r){        sum[rt]=0;        return l;    }    int res;    int m=(l+r)>>1;    if(sum[rt<<1|1]>=v)res=querry(rson,v);    else res=querry(lson,v-sum[rt<<1|1]);    pushup(rt);    return res;}int main(){    int t;    cin>>t;    while(t--){        int n;        scanf("%d",&n);        a[0]=0;        for(int i=1;i<=n;i++)scanf("%d",&a[i]);        build(1,1,n);        for(int i=n;i>=1;i--){            int f=a[i]-a[i-1];            ans[i]=querry(1,1,n,f+1);        }        for(int i=1;i<=n;i++)            printf("%d%c",ans[i],(i==n)?'\n':' ');    }}




0 0
原创粉丝点击