参考文献
洛谷某讨论https://www.luogu.org/discuss/show/158501?page=2
题意
对于一串长度为 n ( n < = 2000 ) n(n<=2000) n(n<=2000)的序列,对于所有的 i , j ( 1 < = i < = n , i < = j < = n ) i,j(1<=i<=n,i<=j<=n) i,j(1<=i<=n,i<=j<=n)区间,求出他们的中位数。
题解
这个ider先在这里占坑吧,毕竟感觉这个思路挺好的。
我们可以一开始把整个序列排序,然后对排序后的数组链表化,并且求出每个点在链表中的下标。
那么对于固定的 l l l,我们的 r r r从大到小查找,我们对于 [ l , n ] [l,n] [l,n]暴力在链表找中位数,然后对于 [ l , n − 1 ] [l,n-1] [l,n−1]我们可以把 n n n在链表中删掉,然后看看中位数向左还是向右,这个是 O ( 1 ) O(1) O(1)的。
然后我们可以 O ( 1 ) O(1) O(1)将 [ l , n ] [l,n] [l,n]的排序链表推到 [ l + 1 , n ] [l+1,n] [l+1,n]的排序链表,不过我们需要拷贝数组,为 O ( n ) O(n) O(n),当然我朋友的代码是递归实现,所以不用拷贝。
代码来自HYY大神的,当然也是他最先提出这个问题。
注意:至于偶数的中位数,貌似这位神犇的代码是取左边的那个数字
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2005;
int a[N],p[N],l[N],r[N],w[N];
bool cmp(int x,int y){return a[x]<a[y];}
int f[N][N];
void dfs(int i,int k,bool bk,int now)
{
if(i<=k)
{
int j=w[k];//printf("%d\n",j);
if(bk){if(j<=now)now=r[now];}
else{if(j>=now)now=l[now];}
int r1=r[l[j]],l1=l[r[j]];//tmp
r[l[j]]=r[j];l[r[j]]=l[j];
f[i][k-1]=a[p[now]];
dfs(i,k-1,bk^1,now/*目前中位数的位置*/);
r[l[j]]=r1;l[r[j]]=l1;
}
}
void make(int n)
{
bool bk;
if(n&1)bk=false;//奇数为false
else bk=true;
int now=(n+1)/2;
f[1][n]=a[p[now]];dfs(1,n,bk,now);
for(int i=1;i<n;i++)
{
int j=w[i];//printf("%d\n",j);
if(bk){if(j<=now)now=r[now];}
else{if(j>=now)now=l[now];}
r[l[j]]=r[j];l[r[j]]=l[j];bk^=1/*奇偶性质*/;//删数
f[i+1][n]=a[p[now]];
dfs(i+1,n,bk,now);//处理中位数
}
}
int main()
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)p[i]=i;
sort(p+1,p+n+1,cmp);
for(int i=1;i<=n;i++)w[p[i]]=i;
for(int i=1;i<=n;i++)l[i]=i-1,r[i]=i+1;
make(n);
for(int i=1;i<=n;i++)
{
for(int j=i;j<n;j++)printf("%d ",f[i][j]);
printf("%d\n",f[i][n]);
}
return 0;
}