【NOIP2014八校联考第4场第2试10.20】乐曲创作

Description

小可可是音乐学院的一名学生,他需要经常创作乐曲完成老师布置的作业。
可是,小可可是一个懒惰的学生。所以,每次完成作业时,他不会重新创作一首新的乐曲,而是去修改上一次创作过的乐曲作为作业交给老师。小可可的乐曲由N个音调不同的音符组成,分别记为音符1…N。因此,他创作的乐曲是由1…N的一个排列构成,例如N=5时,他创作的乐曲可能为:2,1,3,5,4。但是,小可可每一次会按照一定的要求修改上一次创作的乐曲。他规定,修改过后的乐曲必须与上一次创作的乐曲的悦耳值相同。所谓悦耳值就是他所创作的乐曲,也就是1…N的排列中逆序对的个数。逆序对是指对于1…N的一个排列A1,A2,…,An中的两个数Ai,Aj,满足i

Solution

先求出逆序对sum
题目要求的最小字典序,所以要选一个位置p,然后前面保持不变,后面改掉。
那么这个位置要满足一些条件,首先是后面有一个比自己大的,然后就是第一个比自己大的先填后面全部倒着填(最好情况)不比sum小,全部正着填(最坏情况)不比sum大。
那么就找到这个位置了。把后面第一个比自己大的放过来。
然后每次二分填哪个数最优,用线段树查找第k大的数填来搞就好了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=5e5+7;
typedef long long ll;
int i,j,l,n,m,p;
int a[maxn],f[maxn],ans[maxn],tr[maxn],r,mid,o,you[maxn];
ll sum,b[maxn],len,op,k,zhi;
struct node{
    int num;
}t[maxn*4];
bool bz[maxn],az;
int lowbit(int x){return x&(-x);}
void add(int x,int y){
    for(;x<=n;x+=lowbit(x))tr[x]+=y;
}
int find1(int x){
    int z=0;
    for(;x;x-=lowbit(x))z+=tr[x];
    return z;
}
void change(int x,int l,int r,int y,int z){
    if(l==r){t[x].num+=z;return;}
    int mid=(l+r)/2;
    if(y<=mid)change(x*2,l,mid,y,z);
    else change(x*2+1,mid+1,r,y,z);
    t[x].num=t[x*2].num+t[x*2+1].num;
}
int find(int x,int l,int r,int y){
    if(l==r)return l;
    int mid=(l+r)/2;
    if(y<=t[x*2].num)return find(x*2,l,mid,y);
    else return find(x*2+1,mid+1,r,y-t[x*2].num);
}
int main(){
//  freopen("fan.in","r",stdin);
//  freopen("fan.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n)scanf("%d",&a[i]);
    fod(i,n,1)you[i]=max(you[i+1],a[i]);
    fod(i,n,1){
        f[i]=find1(a[i]);sum+=f[i];
        add(a[i],1);
    }
    fo(i,1,n)b[i]=b[i-1]+f[i];
    fod(i,n,1){
        len=n-i+1;
        zhi=b[i-1]+len*(len-1)/2;
        if(zhi>=sum&&you[i]>a[i]&&b[i-1]+f[i]+1<=sum){
            p=i;
            break;
        }
    }
    fo(i,1,p-1)ans[i]=a[i];
    int u=0x7fffffff;
    fo(i,p+1,n)if(a[i]>a[p])u=min(u,a[i]);
    fo(i,p,n)
    if(a[i]!=u)change(1,1,n,a[i],1),op+=(a[i]1];ans[p]=u;
    fo(i,p+1,n){
        l=1,r=n-i+1;len=r-1;
        while(l2;
            o=find(1,1,n,mid);
            k=mid-1+len*(len-1)/2+op;
            if(k>=sum)r=mid;else l=mid+1;
        }
        ans[i]=find(1,1,n,l);
        op+=l-1;
        change(1,1,n,ans[i],-1);
    }
    fo(i,1,n)printf("%d ",ans[i]);
}

你可能感兴趣的:(noip,线段树,贪心)