题目:给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。
样例输入:
7 4
5 7 2 4 3 1 6
样例输出:
4
解释:{4},{7,2,4},{5,7,2,4,3},{5,7,2,4,3,1,6}
先贴一发pascal渣代码……
<pre name="code" class="php">
var
n,b,i,mark,small,big,ans:longint;
left,right:array[-100000..100000] of longint;
a:array[1..100000] of longint;
begin
assign(input,'median.in'); reset(input);
assign(output,'median.out'); rewrite(output);
read(n,b);
for i:=1 to n do
begin
read(a[i]);
if a[i]=b then
mark:=i;
end;
for i:=mark-1 downto 1 do
begin
if a[i]>b then inc(big)
else inc(small);
if big=small then inc(ans);
inc(left[big-small]);
end;
big:=0; small:=0;
for i:=mark+1 to n do
begin
if a[i]>b then inc(big)
else inc(small);
if big=small then inc(ans);
inc(right[small-big]);
end;
for i:=-n to n do
ans:=ans+left[i]*right[i];
write(ans+1);
close(input); close(output);
end.
思路是这样的:根据数据范围n<=100000来看算法得高效一点。因为一个奇数个数的序列中位数为b,所以序列里比b大的数的个数肯定等于比b小的数的个数。假设已经找到了这样一个序列,设中位数左边比它大的数有bigleft个,比它小的数有smallleft个,中位数右边比b大的数有bigright个,比b小的有smallright个。那么bigleft+bigright=smallleft+smallright。移项得到bigleft-smallleft=smallright-bigright.
于是就可以从中位数所在位置mark开始向两端查找,每查到一个数,判断比b大还是比b小,相应地更改big和small的值,left[i]和right[i]分别记录了i=bigleft-smalleft=smallright-bigright出现的次数。注意left和right数组的下标表示的上述式子,因此下标应该是-n..n的范围。于是答案就等于所有的left[i]*right[i]之和。
然后这还不是最后答案……在查找时如果发现某一边的small=right,要inc(ans)。不然会漏解。然后最后输出答案的时候还要+1,因为中位数本身也算一个序列。