首页 > 代码库 > CodeVS 1432-总数统计

CodeVS 1432-总数统计

原题

题目描述 Description

给出n个数,统计两两之和小于k的方案数之和。

输入描述 Input Description

第一行一个数n,表示数字的个数;
第二行到第n + 1行,每行一个不超过2,000,000,000的数k;
第n + 2行一个数m,表示m个问题;
第n + 3行到第n + m + 2行,每行一个数m,询问表示n中两两组合不超过m的组
合的个数;

输出描述 Output Description

输出m行,每行对应一个答案

样例输入 Sample Input

3

1

2

3

2

2

3

样例输出 Sample Output

0

1

数据范围及提示 Data Size & Hint

30%的数据1 ≤ n ≤ 100, 1 ≤ m ≤ 50, k ≤ 2000;
100%的数据 1 ≤ n ≤ 10000, 1 ≤ m ≤ 100, k ≤ 2,000,000,000

 

题解

刚开始看到前面以为是道水题,想打个打暴力水过,看到后面数据怂了,只好再想优化方法。。。

后来还是想到了二分法,但输入时是无序的,要先敲一个快排从小到大排,接着二分是关键。

设一个函数find(l,r,x),表示区间[l,r]中,不超过x的最大值的位置。

接着在每次读入k的时候,求出区间[1,n]中不超过k的最大值的位置p【这步其实不要也可以,只是效率更高】,接着用一个j循环1~p,每次求出find(j,p+1,k-a[j])-j【也就是能与a[j]结成对子的方案数】,再更新ans,输出。

 

技术分享
 1 var a:array[1..10000] of int64;
 2 var n,m,k,p,ans:int64;
 3 var i,j:longint;
 4 function find(l,r,x:int64):int64;//二分
 5 var mid:int64;
 6 begin
 7   while l+1<r do
 8   begin
 9     mid:=(l+r) div 2;
10     if a[mid]<=x then l:=mid else r:=mid;
11   end;
12   exit(l);
13 end;
14 procedure q(l,r:longint);
15 var i,j,m,t:longint;
16 begin
17   i:=l;j:=r;
18   m:=a[(l+r) div 2];
19   repeat
20     while a[i]<m do inc(i);
21     while a[j]>m do dec(j);
22     if i<=j then
23     begin
24       t:=a[i];a[i]:=a[j];a[j]:=t;
25       inc(i);dec(j);
26     end;
27   until i>j;
28   if i<r then q(i,r);
29   if l<j then q(l,j);
30 end;
31 begin
32   readln(n);
33   for i:=1 to n do read(a[i]);q(1,n);//读入&排序
34   readln(m);
35   for i:=1 to m do
36   begin
37     ans:=0;readln(k);
38     p:=find(1,n+1,k);//找出最小的枚举区间
39     for j:=1 to p do ans:=ans+find(j,p+1,k-a[j])-j;//关键代码,好好理解
40     writeln(ans);
41   end;
42 end.
CodeVS 1432-总数统计

 

 

 

 

欢迎转载,请注明出处。

 

CodeVS 1432-总数统计