2015 CCC - 01 统计数对
源:CNUOJ-0384 http://oj.cnuschool.org.cn/oj/home/problem.htm?problemID=354
题目分析:当时拿到这道题第一个想法就是排序后n^2暴力枚举,充分利用好有序这一特性随时“短路”。
read(n);read(m);
int temp; for(int i = ; i < n; i++)
{
read(temp);
if(temp <= m)
{
p[tot].v = temp;
p[tot++].num = i;
}
} sort(p, p + tot); for(int i = ; i < tot - ; i++)
{
for(int j = i + ; j < tot; j++)
{
if(p[i].v + p[j].v > m) break;
else if(p[i].num < p[j].num) ans++;
}
ans %= ;
} printf("%d\n", ans); return ;
算法1
然后很高兴的简单一测试就哭了……为什么答案始终不对呢……

如上图就是一个十分典型的例子(下标为序号),不难发现当我们手算时,我们是算的“2 + 1”,但排完序后计算机执行的是“1 + 2”,序号不符合题意就被砍掉了……
那么怎么解决呢?第一个想法便是优化一下 j 的循环,从头到尾全来一遍,保证不漏解:
read(n);read(m);
int temp; for(int i = ; i < n; i++)
{
read(temp);
if(temp <= m)
{
p[tot].v = temp;
p[tot++].num = i;
}
} sort(p, p + tot); for(int i = ; i < tot; i++)
{
for(int j = ; j < tot; j++)
{
if(i == j) continue;
if(p[i].v + p[j].v > m) break;
else if(p[i].num < p[j].num) ans++;
}
ans %= ;
} printf("%d\n", ans);
算法2
排序是nlogn,枚举是n^2,对于十万的数据量无法吐槽……只能有50分……

好的,接下来我们来分析一下这个算法干了些什么,比如对于3 2 1这个序列:



可以看到,1 -> 2, 2 -> 1 分别别计算过一次,其中红色的一次失败了,蓝色的成功了。(在红色中3 > 2,而在蓝色中是2 < 3满足题意)
也不难得出:每一对数字都被正着计算了一次,反着计算了一次;一次失败,一次成功。
那么:反正判断不判断都是ans++ 一次(仅成功一次,失败的时候不会更新),那为什么要判断呢?
于是,我们编出了下面没有判断编号只判断了大小的程序:
for(int i = ; i < tot - ; i++)
{
for(int j = i + ; j < tot; j++)
{
if(p[i].v + p[j].v > m) break;
ans++;
}
ans %= ;
}
算法3
这个算法常数可以几乎砍掉一半多,但复杂度仍然是n^2的,它的表现……呵呵

进一步的优化就要从查询方法开始了:用十分常见的二分法效果怎么样呢?于是我们终于得到了标程:
#include <iostream> //第一题标程
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std; const int maxn = + ;
int n, m; int p[maxn];
int ans = ; int tot = ; int A[maxn]; void read(int& x)
{
x = ;
char ch = getchar();
int sig = ;
while(!isdigit(ch))
{
if(ch == '-') sig = -;
ch = getchar();
}
while(isdigit(ch))
{
x = x * + ch - '';
ch = getchar();
}
x *= sig;
return ;
} int main()
{
read(n);read(m);
int temp; for(int i = ; i < n; i++)
{
read(temp);
if(temp <= m) p[tot++] = temp;
} sort(p, p + tot); int L, R, M; for(int i = ; i < tot - ; i++)
{
L = i;
R = tot - ; if(p[R] + p[i] <= m)
{
ans += R - i;
continue;
} while(L < R)
{
M = L + R >> ;
//printf("L:%d M:%d R:%d\n", L, M, R);
if(p[i] + p[M] <= m) L = M + ;
else R = M;
} if(L - i > ) ans += L - i - ; //cout << ans << endl;
ans %= ;
} printf("%d\n", ans); return ;
}
从n^2到nlogn是不小的进步,十万的数据专为nlogn而生:

这道题就这样被干掉了……
当然了,其实还可以用treap瞎搞啦:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <ctime>
using namespace std;
const int maxn = + ;
struct Node{
int r, v, s;
Node* ch[];
Node() {}
void maintain(){
s = ch[] -> s + ch[] -> s + ;
return ;
}
}nodes[maxn], *null = &nodes[];
int tot = , n, m, A[maxn];
void read(int& x){
x = ; int sig = ; char ch = getchar();
while(!isdigit(ch)) { if(ch == '-') sig = -; ch = getchar(); }
while(isdigit(ch)) x = * x + ch - '', ch = getchar();
x *= sig; return ;
}
void rotate(Node* &o, int d){
Node* k = o -> ch[d ^ ];
o -> ch[d ^ ] = k -> ch[d];
k -> ch[d] = o;
o -> maintain();
k -> maintain();
o = k;
return ;
}
void init(Node* &o, int v){
o -> ch[] = null;
o -> ch[] = null;
o -> s = ;
o -> r = rand();
o -> v = v;
return ;
}
void insert(Node* &o, int v){
if(o == null){
o = &nodes[++ tot];
init(o, v);
}
else{
int d = v < o -> v ? : ;
insert(o -> ch[d], v); //你大爷
if(o -> ch[d] -> r > o -> r) rotate(o, d ^ );
}
o -> maintain();
return ;
}
void remove(Node* &o, int v){
if(o == null) return ;
if(o -> v == v){
if(o -> ch[] == null) o = o -> ch[];
else if(o -> ch[] == null) o = o -> ch[];
else{
int d = o -> ch[] -> r < o -> ch[] -> r ? : ;
rotate(o, d);
remove(o -> ch[d ^ ], v);
}
}
if(o != null) o -> maintain();
return ;
}
/*int find(Node* &o, int v){
if(o == null) return -1;
if(o -> v == v){
if(o -> ch[0] != null) return o -> ch[0] -> s;
else return 1;
}
int d = v < o -> v ? 0 : 1;
return find(o -> ch[d], v);
}//*/
int rank(Node* &o, int v){
if(o == null) return ;
if(o -> v > v) return rank(o -> ch[], v);
else return rank(o -> ch[], v) + o -> ch[] -> s + ;
}
Node* root = null;
long long ans = ;
void Init(){
srand(time());
null -> s = ;
read(n); read(m);
for(int i = ; i < n; i ++){
read(A[i]);
ans += rank(root, m - A[i]);
//printf("%d ", rank(root, m - A[i]));
insert(root, A[i]);
}
return ;
} void print(Node* &o){
if(o == null) return ;
print(o -> ch[]);
printf("%d ", o -> v);
print(o -> ch[]);
return ;
} int main(){
Init();
printf("%lld\n", ans % );
return ;
}
/*
5 100000
1 2 3 4 5
*/
小结:本题一定要好好读条件,仔细分析哪些可以优化。
考点:二分,坑题
2015 CCC - 01 统计数对的更多相关文章
- Cheatsheet: 2015 10.01 ~ 10.31
.NET Publishing your ASP.NET App to Linux in 5 minutes with Docker Integrating AngularJS with ASP.NE ...
- Cheatsheet: 2015 06.01 ~ 06.30
Web The Front-End Optimization Checklist [ASP.NET 5] Production Ready Web Server on Linux. Kestrel + ...
- Murano Weekly Meeting 2015.12.01
Meeting time: 2015.December.1st 1:00~2:00 Chairperson: Nikolay Starodubtsev, from Mirantis Meeting ...
- Murano Weekly Meeting 2015.09.01
Meeting time: 2015.September.1st 1:00~2:00 Chairperson: Nikolay Starodubtsev, from Mirantis Meeting ...
- 使用jquery脚本获取随笔、文章和评论的统计数,自定义显示位置
为了这个问题,花了好些时间去摸索,无奈没有搞定.于是,我就到博问去提问,终于搞定! 在此,非常感谢SeayXu的热心帮助. 1.在需要的位置添加一个标签 <div id="stats_ ...
- Archlinux 2015.07.01 和 Windows7 双系统 安装教程
提前在windows7下给Archlinux预留一个分区,大小最好在20G以上(根据自己硬盘情况分配). 第一步,安装前的准备 从arch官网下载最新的ISO文件archlinux-2015.07.0 ...
- Cheatsheet: 2015 12.01 ~ 12.31
Mobile Setting Up the Development Environment iOS From Scratch With Swift: How to Test an iOS Applic ...
- Cheatsheet: 2015 11.01 ~ 11.30
Golang Roadomatic: Node vs. Go Quick Guide to Golang for Java Developers 3 Go Gotchas Web Choosing a ...
- Cheatsheet: 2015 09.01 ~ 09.30
Web A Guide to Vanilla Ajax Without jQuery Gulp for Beginners A Detailed Walkthrough of ASP.net MVC ...
随机推荐
- 4 - SQL Server 2008 之 使用SQL语句删除表格
使用删除表格的SQL命令与删除数据的命令一样,只是删除的是表格这个对象, 语法如下:DROP TABLE 表名 一般在删除表格之前,需判断这个表格存不存在,存在则删除,不存在则不进行执行任何代码. 代 ...
- android开发之service详解
service作为android的四大组件之一,其重要性可想而知,在开发中,我们经常把一些不需要与用户进行交互的工作放在service中来完成,service运行在后台,这样有些人可能会产生错觉,以为 ...
- Json字符串与字典互转
#pragma mark 转换json字符串 +(NSString *)toJSON:(id)aParam { NSData *jsonData=[NSJSONSerialization data ...
- Active Desktop--桌面字体背景被修改
怎么修改回来 步骤如下 方法一.在桌面上点击右键 -- 排列图标 -- 去掉“在桌面上锁定Web项目”上的勾. 方法二.右键点击我的电脑 -- 属性 -- 高级 -- 点击“性能”下面的“设置”按钮, ...
- sqlserver 误删数据恢复
----创建存储过程 CREATE PROCEDURE Recover_Deleted_Data_Proc @Database_Name NVARCHAR(MAX) , @SchemaName_n_T ...
- C#使用Process类调用外部程序(转)
在程序开发中,一个程序经常需要去调用其他的程序,C#中Process类正好提供了这样的功能.它提供对本地和远程进程的访问并使您能够启动和停止本地系统进程.一.启动进程实例 Process myProc ...
- 关于get和set访问器以及属性和字段变量的区别问题
属性是对一个或者多个字段的封装. 类里面为什么要用一个共有的属性来封装其中的字段,也可以这样说用共有属性来封装私有变量,其中的好处应该大家都说的出来,就是为了实现数据的封装和保证了数据的安全 ...
- 设置linux服务器定时与时间服务器同步
在一些大公司经常出现这样一个情况:公司或一些机关单位的内部业务系统的应用服务器以及数据都是做的多机集群部署而且基本都是linux系统,而且都是内部网,不与外网通讯的.这样经常就会出现一个情况,我发送任 ...
- 06MySQL数据库入门
1.数据库的概念 数据库是保存数据的仓库,可以方便的把数据放进去,并且把数据根据各种需求取出来. 数据库管理系统(Database Management System,DBMS)是对数据库进行管理(增 ...
- hdoj 2041 超级阶梯
代码: #include <stdio.h>int main(){int n;int i;int m;int count;int dp[50];while(scanf("%d&q ...