弱渣花了好几天终于把这题A了_(:з」∠)_
可能因为大牛们认为这题太简单吧,网上的题解都不怎么带注释。特别是那个S型走法,看的博(ruo)主(cai)一愣一愣的_(:з」∠)_
因此AC后打算整理下大牛们的题解并加上注释,以便查阅_(:з」∠)_
主要参考了下面几篇题解:
http://blog.csdn.net/zhaofukai/article/details/7841327
http://blog.csdn.net/chromer_cn/article/details/7892048
原题:http://acm.hdu.edu.cn/showproblem.php?pid=3648
题意:给出一个n*n的矩阵,现在对于(r+1, r+1) to (n-r, n-r)中每个点,以其为中心的规模为(2r+1)*(2r+1)的子矩阵,替换其中间元素为该子矩阵的中位数。
n<=500 元素<=10^6。
A这道题前首先要知道什么是树状数组和怎么样用树状数组快速求第k小的值。
博(ruo)主(cai)从书上学的树状数组,在这就不安利书籍了,网上也有很多很棒的资料。
至于怎么样用树状数组求第k小的值,博(ruo)主(cai)个人认为这篇讲的挺好的 http://www.cnblogs.com/wuyiqi/archive/2011/12/25/2301071.html
看的时候注意树状数组[1...i]的区间和表示的是“树状数组中小于等于i的数的个数和”就行了。(防止有新手误解,强调一下上面的“区间和”指的是用树状数组求出来的区间和,而不是简单的从下标1开始将每个元素相加知道下标i !!!)
准备好了知识点,就可以做题了。
实际上思路很好想的嘛_(:з」∠)_,将子阵中的数插入到树状数组求中位数,然后再插几行删几行求新子阵的新中位数。不过听说普通的走法会TLE,走S型路线可以最大限度的利用旧子阵,然后就AC了_(:з」∠)_。
有人可能会问k的范围怎么定,这种问题自己画个子阵就不清楚了嘛...
#include<bits/stdc++.h>
#define maxv 1000010
#define maxn 510
using namespace std;
int N,R,mxv;
int table[maxn][maxn],ans[maxn][maxn];
int bit[maxv];//改了一下的树状数组模板,没什么好说的吧?
inline int lowbit(int x){
return x&-x;
}
void add(int i,int x){
while(i<=mxv){
bit[i]+=x;
i+=lowbit(i);
}
}
int find_kth(int k){
int ret=0,cnt=0;
for(int i=20;i>=0;i--){
ret+=(1<<i);
if(ret>=mxv||cnt+bit[ret]>=k) ret-=(1<<i);
else cnt+=bit[ret];
}
return ret+1;
}
void SOLVE()
{
int mid=2*R*R+2*R+1;//子阵的中位数是子阵中第mid小的数
bool pass=true;//S型走法中向右走的那些行
for(int i=R+1;i<=N-R;i++)
if(pass){//向右走
pass=false;
for(int j=R+1;j<=N-R;j++){
if(j==R+1)//第一格特殊处理,即往下走,删旧的一行加新的一行
for(int k=j-R;k<=j+R;k++)
add(table[i-R-1][k],-1),
add(table[i+R][k],1);
else//向右走,删旧的一列加新的一列
for(int k=i-R;k<=i+R;k++)
add(table[k][j-R-1],-1),
add(table[k][j+R],1);
ans[i][j]=find_kth(mid);//求中位数
}
}else{//向左走
pass=true;
for(int j=N-R;j>=R+1;j--){
if(j==N-R)//第一格特殊处理,即往下走,删旧的一行加新的一行
for(int k=j-R;k<=j+R;k++)
add(table[i-R-1][k],-1),
add(table[i+R][k],1);
else //向左走,删旧的一列加新的一列
for(int k=i-R;k<=i+R;k++)
add(table[k][j+R+1],-1),
add(table[k][j-R],1);
ans[i][j]=find_kth(mid);//求中位数
}
}
for(int i=R+1;i<=N-R;i++)
for(int j=R+1;j<=N-R;j++)
printf("%d%s",--ans[i][j],j==N-R?" \n":" ");
}
bool INPUT()
{
scanf("%d%d",&N,&R);
if(!N&&!R) return false;
mxv=-1;
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
scanf("%d",&table[i][j]),
mxv=max(mxv,++table[i][j]);//0的lowbit还是0,自然没办法插入,处理一下就可以了(*)
memset(bit,0,sizeof bit);
//插入第一个子阵,不过为了SOLVE中的走法统一,这里插入的是第一个子阵的上面一格的那个子阵
for(int i=0;i<=(R<<1);i++)
for(int j=1;j<=(R<<1)+1;j++)
add(table[i][j],1);
return true;
}
void MAIN()
{
for(int i=0;i<maxn;i++) table[0][i]=1;//和(*)一样的道理
while(INPUT())
SOLVE();
}
int main()
{
//freopen("in#pro.txt","r",stdin);
//freopen("out.txt","w",stdout);
MAIN();
return 0;
}