【题解】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;
}

最后感慨一句,数学真妙啊!

原文地址:https://www.cnblogs.com/Shayndel/p/10164534.html