[BZOJ1303][CQOI2009]中位数图
Solution
有点意思的思维题。
首先考虑到一个满足b是中位数的子序列中大于b和小于b的数字的个数一定是相等的。
设b在排列中的位置为p,可以对于每一个在p左边的位置i求出序列[i,p-1]中小于b的个数和大于b的个数,记为cnt1和cnt2;同理对于每一个在p右边的位置j求出序列[p+1,j]求出相应的cnt1和cnt2。
如果直接枚举i,j判断两者的cnt1和cnt2加起来是否相等是n^2的,会T。
cnt1和cnt2有两维,不方便比较,稍加思考会发现其实可以转化成一维。
即:
$cnt1+cnt1'=cnt2+cnt2'$ $\Longleftrightarrow$ $cnt1-cnt2=cnt2'-cnt1'$
所以我们只需要记个差值就可以了。
于是按照套路,先把左端点的差值存multiset,然后枚举右端点,O(logn)查询即可。
但是这样计算的只是跨过p位置的连续子序列,不要忘了加上以p为右端点的左边子序列,和以p为左端点的右边子序列。
还有发现差值的范围在-N~N之间,于是multiset都不用了,开个桶就好了(我真是个zz)。
于是O(n)AC了这题。
Code
#include<bits/stdc++.h> using namespace std; const int N=1e5+5,D=1e5;//有负数下标,整体加D int n,b,p,l,r,cnt1,cnt2,ans,a[N],s[N<<1]; int main(){ cin>>n>>b; for(int i=1;i<=n;++i){ cin>>a[i]; if(a[i]==b) p=i,++ans; } for(int i=p-1;i>=1;--i){ a[i]<b?++cnt1:++cnt2; ++s[cnt1-cnt2+D]; ans+=cnt1==cnt2; } cnt1=cnt2=0; for(int i=p+1;i<=n;++i){ a[i]<b?++cnt1:++cnt2; ans+=(cnt1==cnt2)+s[cnt2-cnt1+D]; } cout<<ans<<endl; return 0; }
愿你有一天能和重要的人重逢