2、数学,又是数学(mathagain)
其实吧,大家也发现了,这六道题本来是一科一道的,但是呢,由于某某人比较……,就不顾某某人的反对抛弃了我们可爱的English,又出了一道math……
题目描述
Liukeke同学(我们敬爱滴亲爱滴可爱滴班长大人)最近又在忙着开班会(真是个工作狂),于是乎,数学学案就被抛到了九霄云外,直到预备铃打完,他才发现那张美丽的雪白的学案,眼看一场血案即将发生,他请你用最快的速度帮他算完第一题:
给定一个数串,数串的长度为n,现在将一个子串的每个数字之和定义为该子串的数串和,请你求出数串中有多少个子串的数串和为正数。
输入格式
第一行一个数n,表示数串的长度。
第二行一共n个数,表示数串中的每个数
输出格式
就一个数,表示数串中有多少个子串的数串和为正数。
样例输入
3
8 -9 2
样例输出
3
数据范围
30% n<=1000。
100% n<=100000。
Liukeke 的学科试题之五·数学2。(因为此人实在不知道怎么出英语了,⊙﹏⊙b)
预处理出累和,不难发现,只要前一个和小于后一个,这两个的差所表示的那一段数列的和就是正数。
然后,就对累和的那个数列归并求顺序对。好吧,我承认我当时把这个算法给忘了。
代码(Liukeke)
var
a,b:array[0..100000] of longint;
i,n,t:longint;ans:int64;
procedure merge(l,r:longint);
var mid,i,j,k:longint;
begin
if l=r then exit;
mid:=(l+r)>>1;
merge(l,mid);merge(mid+1,r);
i:=l; j:=mid+1; k:=l;
while (i<=mid) and(j<=r) do
begin
if a[i]>=a[j] then begin inc(ans,i-l);b[k]:=a[j];inc(k);inc(j);end
else begin b[k]:=a[i]; inc(k);inc(i);end;
end;
if i<=mid then for j:=i to mid do
begin
b[k]:=a[j];inc(k);
end
else for i:=j to r do
begin
b[k]:=a[i];inc(k);
inc(ans,mid-l+1);
end;
for i:=l to r do a[i]:=b[i];
end;
begin
assign(input,'mathagain.in');reset(input);
assign(output,'mathagain.out');rewrite(output);
readln(n);
fillchar(a,sizeof(a),0);
for i:=1 to n do
begin
read(t);
a[i]:=t+a[i-1];
end;
merge(0,n);
writeln(ans);
close(input);close(output);
end.
补充:归并求逆序对。
type
arr=array[0..40000] of longint;
var
temp,a,b:arr;
c:array[0..200,0..200] of longint;
i,j,k,m,n,ans1,ans2:longint;
procedure hba(l,mid,r:longint);
var
i,j,k,kk:longint;
begin
i:=l; j:=mid+1;
for kk:=l to r do
begin
if (i<=mid)and((a[i]<=a[j])or(j>r)) then
begin
temp[kk]:=a[i];
inc(i);
end
else
begin
temp[kk]:=a[j];
inc(j);
if i<mid+1 then inc(ans1,mid-i+1);
end;
end;
for kk:=l to r do
a[kk]:=temp[kk];
end;
procedure mergea(s,t:longint);
var
k:longint;
begin
if s=t then exit;
k:=(s+t)>>1;
if k>s then mergea(s,k);
if t>k+1 then mergea(k+1,t);
hba(s,k,t);
end;
procedure hbb(l,mid,r:longint);
var
i,j,k,kk:longint;
begin
i:=l; j:=mid+1;
for kk:=l to r do
begin
if (i<=mid)and((b[i]<=b[j])or(j>r)) then
begin
temp[kk]:=b[i];
inc(i);
end
else
begin
temp[kk]:=b[j];
inc(j);
if i<mid+1 then inc(ans2,mid-i+1);
end;
end;
for kk:=l to r do
b[kk]:=temp[kk];
end;
procedure mergeb(s,t:longint);
var
k:longint;
begin
if s=t then exit;
k:=(s+t)>>1;
if k>s then mergeb(s,k);
if t>k+1 then mergeb(k+1,t);
hbb(s,k,t);
end;
begin
assign(input,'game.in'); reset(input);
assign(output,'game.out'); rewrite(output);
while not eof do
begin
readln(n,m);
for i:=1 to n do
begin
for j:=1 to n do read(c[i,j]);
readln;
end;
if odd(m) then
begin
for i:=1 to n do
for j:=1 to n do
a[(i-1)*n+j]:=c[i,j];
for j:=1 to n do
for i:=1 to n do
b[(j-1)*n+i]:=c[i,j];
end
else
begin
for i:=1 to n do
for j:=1 to n do
b[(i-1)*n+j]:=c[i,j];
for j:=1 to n do
for i:=1 to n do
a[(j-1)*n+i]:=c[i,j];
end;
ans1:=0; ans2:=0;
mergea(1,n*n);
mergeb(1,n*n);
if ans1=ans2 then
writeln('NYY and XYY will try again')
else
if ans1<ans2 then
writeln('NYY wins the Rocket''s Game')
else
writeln('XYY wins the Rocket''s Game');
writeln;
end;
close(input); close(output);
end.
注:题目中要求求两遍逆序对,就是用归并排序做。需要注意的点:在处理的数据比较多的时候不要再合并时fillchar temp,在递归的时候判断一下大小(红的地方),不然可能栈溢出。