题意
题解
注:本文的代码中的输出不严格正确,反正loj的数据也没多强,不会PE。如果后面加强了当我没说
做法1
没错,第一个还是我的做法。
记得是哪年的SCP还是NOIP初赛竟然程序题就是这个思路,然后我照搬了。
我们这道题目反着考虑,它让我们加数,我们就删除数字,从后往前删除数字,我们先建一个排好序的链表,然后从中一个个删除数字,然后通过链表来跳,由于每次最多删除两个数字,除2就是一个,所以链表每次最多跳一次,时间复杂度:(O(nlogn))。
当然有一种情况要特殊考虑一下,就是当(n=2)时删除到了中位数的位置,这种情况要处理一下,其他也就没什么了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 11000
using namespace std;
struct fuck
{
int x,y;
}a[N];
inline bool cmp(fuck x,fuck y){return x.x<y.x;}
struct node
{
int l,r,x/*权值*/;
}b[N];
void del(int x)
{
b[b[x].l].r=b[x].r;
b[b[x].r].l=b[x].l;
}
int n,be[N];
int list[N],top;
int main()
{
int T;scanf("%d",&T);
while(T--)
{
top=0;
int t;scanf("%d%d",&t,&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].x);
a[i].y=i;
b[i].l=i-1;b[i].r=i+1;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)be[a[i].y]=i,b[i].x=a[i].x;//确定其在链表中的位置
int ans=n/2+1,l=n/2/*左边有多少个数字*/,r=n/2/*右边有多少个数字*/;
for(int i=n;i>=1;i--)//按顺序删除
{
if(i%2==1)
{
if(l<r)
{
ans=b[ans].r;l++;r--;
}
else if(l>r)
{
ans=b[ans].l;l--,r++;
}
list[++top]=b[ans].x;
}
if(be[i]==ans)//刚好卡在中位数的位置
{
if(l>r)ans=b[ans].l,l--,r++;
else if(l<=r)ans=b[ans].r,l++,r--;
}
if(be[i]>ans)r--;
else l--;
del(be[i]);
}
//后面就是输出的问题了。
printf("%d %d
",t,n/2+1);
int now=0;
for(int i=top;i>=1;i--)
{
now++;
if(now>10)
{
now-=10;
printf("
");
}
printf("%d",list[i]);
if(now!=10)printf(" ");
}
printf("
");
}
return 0;
}
做法2
https://www.acwing.com/solution/content/838/
搞两个堆,然后不断插入,如果(size)之差(≥2)了,时间复杂度:(O(nlogn))。(当然,两个堆的做法也可以有其他实现方法(例如像做法1一样,等到奇数再调节,而且最多调节一次),但本质上都是通过两个堆求中间的数字。)
时间复杂度:(O(nlogn))
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
struct cmp1
{
bool operator ()(int &a,int &b)
{
return a>b;//小根堆,不是大根堆
}
};
priority_queue <int,vector<int>, cmp1> q1,kong1;
priority_queue <int> q2,kong2;
void init()
{
int t,x,n,now;
cin>>t;
while(t--)
{
cin>>x>>n;
cout<<x<<" "<<(n+1)/2<<endl;
q1=kong1;
q2=kong2;
int cnt=0;
for (int i=1;i<=n;i++)
{
cin>>now;
if(q1.empty())
q1.push(now);
else
{
if(now>q1.top())
q1.push(now);
else
q2.push(now);
while(q1.size()<q2.size())
{
q1.push(q2.top());
q2.pop();
}
while(q1.size()>q2.size()+1)
{
q2.push(q1.top());
q1.pop();
}
}
if (i&1)
{
cnt++;
cout<<q1.top()<<" ";
if (!(cnt%10))
cout<<endl;
}
}
puts("");
}
}
int main()
{
init();
return 0;
}
作者:秦淮岸灯火阑珊已退役
链接:https://www.acwing.com/solution/content/838/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
做法3
我们考虑用线段树来找到中位数是哪个,对于第(i)次而言,我们就是要找到(frac{i}{2}+1)大的数组,那么能不能让这个这么大的数字自动找上门来呢?
我们先让(t[i]=frac{i}{2}+1)
我们排序一遍,然后从小到大看,如果这个数字(x)原本就是在第(i)次及之前插入的,我们就把(t[i]--),如果(t[i]=0)了,那么这个就是中位数。
不难发现,如果一个数字是第(i)次插入的话,那么(t[i])~(t[n])都要减一。
那么我们只要用线段树管理(t)数组不就行了吗?
但是怎么处理(t[i]=0)的情况呢?
我们暴力储存正数最小值,当正数最小值等于(0)时就暴力进入这个区域重复此操作,把为(0)的数拿出来同时更新最小值。
时间复杂度:对于管理([l,r])区间的节点,他最多有(r-l+1)次进入机会,所以全部节点的范围加起来,就是(O(nlogn))啦。
当然,这也能解决每一次插入求的不是中位数,而是第(k)大的题目。
当然,由于这道题目只是当奇数的时候再查询,所以可以处理一下减少常数,嫌麻烦也可以直接全部偶数设为inf,这样也没有多大问题。
没有代码。
做法4
平衡树他不香吗。(求(frac{i}{2}+1)大值)
时间复杂度:(O(nlogn))