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

来源:互联网 发布:sqlserver数据库安全 编辑:程序博客网 时间:2024/05/18 12:02

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<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>#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]<u);    op+=b[p-1];ans[p]=u;    fo(i,p+1,n){        l=1,r=n-i+1;len=r-1;        while(l<r){            mid=(l+r)/2;            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]);}
1 0
原创粉丝点击