首页 > 代码库 > 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-总数统计
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。