首先,测试数据很大,对每一个测试数据都sort一遍只能拿一点样例分,所以我们得想办法优化为nlogn。
寻找特性:
1.样例可简化为一段连续的0/1操作。
比如输入:0 6,0 8,0 7,1 3 ,1 4
可简化为0 8,1 3.
因为对于连续的0操作/1操作,里面有重复的步骤,比如我们先排0 6,再排0 8,会发现结果与只排0 8的结果一样,说明0 6操作是没有必要的,可以删除,所以0 6,0 8,0 7等价于只进行0 8。同理有1 3,1 4操作等价于1 3.
简而言之,对于连续的0或1操作,我们只需要保留区间长度最大的那个操作即可。
2.第一个操作必然是0操作
因为序列本身是升序的,第一步进行1操作没有什么意义。
3.在某个0/1操作中,整个序列的两端部分数字会被固定住,即不会参与后续的排序。
比如:
【0,9】:987654321
【1,2】:9 12345678
【0,7】:9 65432178
【1,4】:965 123478
【0,6】:965 321478
可发现被划掉的数字是不会变的。
因为我们的0或1操作是在前面的0/1操作的基础上进行排序的,即它们公共的部分不用排。比如【1,4】,对下标4到9排序,发现之前的7,8不用移动。且这种性质会一直存在,原因如下:
对于之后的0/1操作,我们不难看出,只有“中间的公共区域”在一直变化(在此我们不妨定义其为“乱序区”,如【0,7】和【1,4】的乱序区为1234),我们更改一下上面的样例:
【0,9】:987654321
【1,2】:9 12345678
【0,6】:9 54321678
【1,4】:954 123678
【0,7】:9 65432178
这个样例等价于下面这个:
【0,9】:987654321
【1,2】:9 12345678
【0,7】:9 65432178
会发现中间的【0,6】和【1,4】变成没必要的操作而被我们删去了。因为【0,7】这里的下标7囊括了【0,6】和【1,4】的乱序区,中间的操作变得没必要了。这说明了什么?说明了只要当前的0或1操作囊括了之前的0/1操作的乱序区,这个0/1操作就可被删去。
我们记录下保留的0/1操作,可发现乱序区是递减的(因为如果一个小的乱序区后面有大的乱序区的话,它会被替代)。
所以我们可以总结一下优化后的0/1操作特点是:
1.先0操作再1操作,接着又是0操作,以此类推
2.两端的数字不会改变,只有中间的区域会改变,且中间的区域会逐渐往中间靠拢变小。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int,int> PII;
PII s[100005];
int n,m;
int top;
int ans[100005];
int main() {
cin>>n>>m;
int x,y;
for(int i=1; i<=m; ++i) {
scanf("%d%d",&x,&y);
if(!x)//0操作
{
if(top&&s[top].first==0) {//对于连续的0操作只保留一个
y=max(y,s[top--].second);
}
while(top>=2&&s[top-1].second<=y) {//0操作覆盖掉乱序区
top-=2;
}
s[++top]= make_pair(0,y);//插入
}else if(top)//栈不为空,且为1操作。
{
//因为是0/1是交替出现的,且只会有一个,所以这里取最小值即可。
if(top&&s[top].first==1) {
y=min(y,s[top--].second);
}
while(top>=2&&s[top-1].second>=y) {
top-=2;
}
s[++top]= make_pair(1,y);
}
}
int k=n,l=1,r=n;
for(int i=1; i<=top; ++i) {//将固定的数字插入ans数组里
if(s[i].first==0) {
while(r>s[i].second&&l<=r) ans[r--]=k--;
} else {
while(l<s[i].second&&l<=r) ans[l++]=k--;
}
if(l>r) break;
}
//如果有数字没被固定,就还需要将它们存入ans数组里
if(top%2!=0) {
while(k>0) ans[l++]=k--;
} else {
while(k>0) ans[r--]=k--;
}
for(int i=1;i<=n;++i)
{
printf("%d ",ans[i]);
}
return 0;
}
解释一下代码块:
if(top%2!=0) {
while(k>0) ans[l++]=k--;
} else {
while(k>0) ans[r--]=k--;
}
比如:
【0,9】:987654321
【1,2】:9 12345678
【0,7】:9 65432178
【1,4】:965 123478
【0,6】:965 321478
之前的循环结束后,会发现中间的321并没有存入ans数组里。这里是用来防止这种情况发生的。
注意:l与r指针是用来指向固定好的数字的,从两端向中间靠拢,所以当它们相遇后代表所有数字都排好序了,就结束循环。