乐曲创作(music)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了乐曲创作(music)相关的知识,希望对你有一定的参考价值。

乐曲创作(music)

小可可是音乐学院的一名学生,他需要经常创作乐曲完成老师布置的作业。
可是,小可可是一个懒惰的学生。所以,每次完成作业时,他不会重新创作一首新的乐曲,而是去修改上一次创作过的乐曲作为作业交给老师。小可可的乐曲由N个音调不同的音符组成,分别记为音符1…N。因此,他创作的乐曲是由1…N的一个排列构成,例如N=5时,他创作的乐曲可能为:2,1,3,5,4。但是,小可可每一次会按照一定的要求修改上一次创作的乐曲。他规定,修改过后的乐曲必须与上一次创作的乐曲的悦耳值相同。所谓悦耳值就是他所创作的乐曲,也就是1…N的排列中逆序对的个数。逆序对是指对于1…N的一个排列A1,A2,...,An中的两个数Ai,Aj,满足iAj,例如:2,1,3,5,4 这个排列中有2个逆序对,分别为:(2,1),(5,4)。可是,满足条件的排列有很多,小可可会选择在这些满足条件的排列中字典序大于上次创作乐曲的排列的字典序,且字典序尽量小的那一个排列作为新的乐曲。这里的字典序指:排列A:A1,A2…An和排列B:B1,B2…Bn,若存在一个数k,使得Ak由于小可可最近要参加学校的篮球比赛,他没有空余时间完成老师布置的作业,于是他希望作为他好友的你帮助他完成作业。

 

输入格式:

第1行1个正整数N,表示小可可的乐曲由N个音调不同的音符组成。
第2行为N个不同的正整数,表示1…N的一个排列,即小可可上次创作的乐曲。

 

输出格式:

输出只有一行,为1...N的一个排列,表示你帮助小可可修改后的乐曲。
数据保证有满足条件的解。

 

样例输入:

样例一
3
2 3 1
样例二
5
2 1 3 5 4

 

样例输出:

样例一
3 1 2
样例二
2 1 4 3 5

 

数据范围:

30%的数据,1≤N≤10;
60%的数据,1≤N≤1000;
100%的数据,1≤N≤500000。

 

时间限制:

1s

 

空间限制:

256M

 

题目大意不用解释了.一开始的想法就是去构造这样一个排列,然后造不来,写了枚举,TLE30,还算可以的啦.其实,构造还是有点难度的,特别是在细节的方面.

因为题目中涉及到字典序问题,要比原排列大且最小,那么,我们肯定改的越后面越好.

要满足上面两个要求(比原排列大且最小),我们要找到一个位置,要使这个位置可行,必须要满足:

1.这个位置后有某个位置上的数大于它(满足比原排列大的要求)

2.这个比较复杂,下文会提到(满足最小的要求)

3.假设当前位置是i,用S[i]表示i~n的逆序对总数量,T[i]表示对于第i位与i+1~n的逆序对数量.

若要从第i位开始改动(前i-1位不改动),设改动后i~n逆序对个数位cnt,则cnt要满足在区间[Min,Max].

Min怎么求?

即把第i位数x与后面大于x的值中的最小值交换后,再把i+1~n为升序排列的逆序对数目.

因为此时后i+1~n位间逆序对数为0,而交换上来的是大于x中的最小的,显然在满足要求的情况下,这样产生的逆序对数最少,可以通过比较比x大的最小的和比x大的第二小的来得出结论.

Max怎么求?

即从i+1~n以降序得到的逆序对+第i位与第i位后面的逆序对数+1.

为什么要加1?

 若A[i]符合以上条件,则将A[i]与A[j]交换后并能够造出一种逆序对数目相同的方案,其中A[j]为i+1~n中大于A[i]的最小值。

证明如下:

1.将A[j]放上来后由A[j]产生的逆序对数目比之前多1(显然),而后面部分产生的逆序对数又能达到最大(降序排列),即最大值>=cnt.

2.因为上面的条件中有cnt>=T[i]+1,所以当A[j]换上来后逆序对数的最小值小于等于原逆序对数,所以一定有一种方案使得A[j]与A[i]交换后逆序对数不变。

所以,为什么要加1,就是A[i]和A[j]交换后产生的1对逆序对(因为交换后不满足i<j且A[i]<A[j])

 最后,当我们确定了可行位置i之后(即1~i-1的位置不需变动),我们要怎么办?

首先,就是将A[i]和A[j]交换一下,其次,我们现在只需要使i+1~n这段构造出S[i]-T[i]-1个逆序对就行了.

怎么构造?(还要满足字典序的约束)

尽量将后面的反序.

我们现将i+1~n由小到大排成有序,如下:

技术分享

然后,有些情况,这样子做就可以了:

技术分享

但是,有时候,将上面(抽象)的操作做到极限后,还会需要一些逆序对(零头),那么需要这样做:

技术分享

即把A[u]和A[v]换一下,然后像前面的情况一样将后面一段从大到小排序,这样就在u~v这一段制造了v-u个逆序对.

同理,令x为零头,i为上图的u(即i+1~n需要由大到小排),则在排序之前,要swap(A[i],A[i+x]),这样刚好能把所有逆序对消掉.

技术分享
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define LL long long
 5 using namespace std;
 6 const int maxn=500005;
 7 int n,a[maxn],c[maxn],T[maxn],Mx[maxn];
 8 LL S[maxn];
 9 int read(){
10     int x=0; char ch=getchar();
11     while (ch<0||ch>9) ch=getchar();
12     while (ch>=0&&ch<=9) x=x*10+ch-0,ch=getchar();
13     return x;
14 }
15 bool cmp(int x,int y){return x>y;}
16 void U(int x){for (int i=x; i<=n; i+=i&(-i)) c[i]++;}
17 int Q(int x){int ret=0; for (int i=x; i; i-=i&(-i)) ret+=c[i]; return ret;}
18 void A(int p,LL N){
19     sort(a+p,a+1+n);
20     for (int i=p; i<n; i++){
21         LL t=(LL)(n-i)*(n-i-1)/2;
22         if (t<N){
23             swap(a[i],a[i+N-t]); sort(a+1+i,a+1+n,cmp);
24             for (int i=1; i<=n; i++) printf("%d ",a[i]);
25             return;
26         }
27     }
28     for (int i=1; i<=n; i++) printf("%d ",a[i]);
29 }
30 int main(){
31     n=read(),a[n+1]=1e9;
32     for (int i=1; i<=n; i++) a[i]=read();
33     memset(Mx,0,sizeof Mx),Mx[n]=a[n];
34     for (int i=n-1; i>=1; i--) Mx[i]=max(Mx[i+1],a[i+1]);
35     memset(c,0,sizeof c),S[n+1]=0;
36     for (int i=n; i>=1; i--) U(a[i]),T[i]=Q(a[i]-1),S[i]=S[i+1]+T[i];
37     for (int i=n-1; i>=1; i--) if (Mx[i]>a[i]&&((LL)(n-i)*(n-i-1)/2+T[i]+1>=S[i]&&T[i]+1<=S[i])){
38         int p=n+1; for (int j=i+1; j<=n; j++) if (a[j]>a[i]&&a[j]<a[p]) p=j; swap(a[i],a[p]);
39         A(i+1,S[i]-T[i]-1); return 0;
40     }
41     return 0;
42 }
View Code

 






以上是关于乐曲创作(music)的主要内容,如果未能解决你的问题,请参考以下文章

POJ1743 Musical Theme

POJ1743 Musical Theme-后缀数组

poj 1743 Musical Theme(最长重复子串 后缀数组)

超级钢琴

全方位音乐创作工作室——GarageBand for Mac最新版

yytkss 电吉他进阶(one D)