【题解】BZOJ 1303: [CQOI2009]中位数图
BZOJ 1303: [CQOI2009]中位数图
原题传送门
解题思路:
最初的想法是先把数据离散化, 把中位数标为0,比中位数大的标为1,比它小的标为-1。这样,问题就变成了,有多少个元素个数为奇数个且和为0的区间数。找到中位数所在的位置,两个for循环就好。
然后就兴冲冲地写好了代码,不出意外TLE,这才注意到,N<=100000。N^2的时间复杂度直接炸掉。
朴素代码了解一下:
int add(int x,int y){
int s=0;
for(int i=x;i<=y;i++){
s+=f[i];
}
return s;
}//统计
for(int i=qwq;i>=1;i--)
{
for(int j=qwq;j<=n;j++)
{
if(add(i,j)==0)
{
ans++;
}
}
}
然后就开始思考优化的问题,经过onglu大佬的神奇启发,想到一个cnt数组来记录当前所有数字的和,即cnt[i]=cnt[0]+cnt[1]+......cnt[i],那么只要cnt[i]==0并且i为奇数,肯定满足,但是其他情况也需要考虑。
首先元素个数必定为奇数,就用两个数组来记录。a数组:当i为奇数时,前i个数的和是否出现过;b数组记录当i为偶数时,前i个数的和是否出现过。若对于同一个数,在数组a中出现过,在数组b中也出现过,那么说明肯定存在和为0的区间,这时ans++,问题就解决了。
AC代码
#include <iostream>
#include <cstring>
using namespace std;
const int maxn=100000+3;
int f[maxn],a[maxn*2+1],b[maxn*2+1];
int main(){
int n,k,x,qwq,ans=0;
cin>>n>>k;
memset(f,0,sizeof(n+3));
memset(a,0,sizeof(n*2+3));
memset(b,0,sizeof(n*2+3));
for(int i=1;i<=n;i++){
cin>>x;
if(x>k)
qwq=1;
else if(x<k)
qwq=-1;
else
qwq=0;
f[i]+=f[i-1]+qwq;
if(f[i]==0&&i%2)
ans++;
if(i%2)
{
ans+=a[f[i]+n];
b[f[i]+n]++;
}
else
{
ans+=b[f[i]+n];
a[f[i]+n]++;
}
}
cout<<ans<<endl;
return 0;
}
最后感慨一句,数学真妙啊!