codeforces 1461D,离线查询是什么神仙方法,为什么快这么多?
大家好,欢迎来到codeforces专题。
今天我们选择的题目是1461场次的D题,这题全场通过了3702人,从难度上来说比较适中。既没有很难,也很适合同学们练手。另外它用到了一种全新的思想是在我们之前的文章当中没有出现过的,相信对大家会有一些启发。
链接:https://codeforces.com/contest/1461/problem/D
废话不多说了,让我们开始吧。
题意
我们给定包含n个正整数的数组,我们可以对这个数组执行一些操作之后,可以让数组内元素的和成为我们想要的数。
我们对数组的执行操作一共分为三个步骤,第一个步骤是我们首先计算出数组的中间值mid。这里mid的定义不是中位数也不是均值,而是最大值和最小值的均值。也就是mid = (min + max) / 2。
得出了mid之后,我们根据数组当中元素的大小将数组分成两个部分。将小于等于mid的元素分为第一个部分,将大于mid的元素分为第二个部分。这样相当于我们把原来的大数组转化成了两个不同的小数组。
现在我们一共有q个请求,每个请求包含一个整数k。我们希望程序给出我们能否通过上述的操作使得最终得到的数组内的元素和等于k。
如果可以输出Yes,否则输出No。
样例
首先输入一个整数t,表示测试数据的组数()。
对于每一组数据输入两个整数n和q,n表示数组内元素的数量,q表示请求的数量()。接着第二行输入一行n个整数,其中的每一个数,都有。
接下来的q行每行有一个整数,表示我们查询的数字k(),保证所有的n和q的总和不超过。
对于每一个请求我们输出Yes或No表示是否可以达成。

对于第一个样例,我们一开始得到的数组是[1, 2, 3, 4, 5]
。我们第一次执行操作,可以得到mid = (1 + 5) / 2 = 3。于是数组被分为[1, 2, 3]
和[4, 5]
。对于[1, 2, 3]
继续操作,我们可以得到mid = (1 + 3) / 2 = 2,所以数组可以分成[1, 2]
和[3]
。[1, 2]
最终又可以拆分成[1]
和[2]
。
我们可以发现能够查找到的k为:[1, 2, 3, 4, 5, 6, 9, 15]
。
题解
这道题并不算很复杂,解法还是比较清晰的。
我们很容易发现对于数组的操作其实是固定的,因为数组当中的最大值和最小值都是确定的。我们只需要对数组进行排序之后,通过二分查找就可以很容易完成数组的拆分。同样,对于数组的求和我们也不用使用循环进行累加运算,通过前缀和很容易搞定。
所以本题唯一的难度就只剩下了如何判断我们要的k能不能找到,其实这也不复杂,我们只需要把它当成搜索问题,去搜索一下所有可以达到的k即可。这个是基本的深搜,也没有太大的难度。
bool examine(int l, int r, int k) {
if (l == r) return (tot[r] - tot[l-1] == k);
// 如果[l, r]的区间和已经小于k了,那么就没必要去考虑继续拆分了
if (l > r || tot[r] - tot[l-1] < k) {
return false;
}
if (tot[r] - tot[l-1] == k) {
return true;
}
// 中间值就是首尾的均值
int m = (nums[l] + nums[r]) / 2;
// 二分查找到下标
int md = binary_search(l, r+1, m);
if (md == r) return false;
return examine(l, md, k) | examine(md+1, r, k);
}
这段逻辑本身并不难写,但是当我们写出来之后,发现仍然不能AC,会超时。我当时思考了很久,终于才想明白问题出在哪里。
问题并不是我们这里搜索的复杂度太高,而是搜索的次数太多了。q最多情况下会有,而每次搜索的复杂度是。因为我们的搜索层数是,加上我们每次使用二分带来的,所以极端的复杂度是,在n是的时候,这个值大概是,再加上一些杂七杂八的开销,所以被卡了。
为了解决这个问题,我们引入了离线机制。
这里的离线在线很好理解,所谓的在线查询,也就是我们每次获得一个请求,查询一次,然后返回结果。而离线呢则相反,我们先把所有的请求查询完,然后再一个一个地返回。很多同学可能会觉得很诧异,这两者不是一样的么?只不过顺序不同而已。
大多数情况下的确是一样的,但有的时候,我们离线查询是可以批量进行的。比如这道题,我们可以一次性把所有可以构成的k通过一次递归全部查出来,然后存放在set中。之后我们只需要根据输入的请求去set当中查询是否存在就可以了,由于查询set的速度要比我们通过递归来搜索快得多。这样就相当于将q次查询压缩成了一次,从而节约了运算的时间,某种程度上来说也是一种空间换时间的算法。
我们来看代码,获取更多细节:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <string>
#include <map>
#include <set>
#include <algorithm>
#include "time.h"
#include <functional>
#define rep(i,a,b) for (int i=a;i<b;i++)
#define Rep(i,a,b) for (int i=a;i>b;i--)
#define foreach(e,x) for (__typeof(x.begin()) e=x.begin();e!=x.end();e++)
#define mid ((l+r)>>1)
#define lson (k<<1)
#define rson (k<<1|1)
#define MEM(a,x) memset(a,x,sizeof a)
#define L ch[r][0]
#define R ch[r][1]
const int N=100050;
const long long Mod=1000000007;
using namespace std;
int nums[N];
long long tot[N];
set<long long> ans;
int binary_search(int l, int r, int val) {
while (r - l > 1) {
if (nums[mid] <= val) {
l = mid;
}else {
r = mid;
}
}
return l;
}
// 离线查询,一次把所有能构成的k放入set当中
void prepare_ans(int l, int r) {
if (l > r) return ;
if (l == r) {
ans.insert(nums[l]);
return ;
}
ans.insert(tot[r] - tot[l-1]);
int m = (nums[l] + nums[r]) / 2;
int md = binary_search(l, r+1, m);
if (md == r) return ;
prepare_ans(l, md);
prepare_ans(md+1, r);
}
int main() {
int t;
scanf("%d", &t);
rep(z, 0, t) {
ans.clear();
MEM(tot, 0);
int n, q;
scanf("%d %d", &n, &q);
rep(i, 1, n+1) {
scanf("%d", &nums[i]);
}
sort(nums+1, nums+n+1);
rep(i, 1, n+1) {
tot[i] = tot[i-1] + nums[i];
}
prepare_ans(1, n);
rep(i, 0, q) {
int k;
scanf("%d", &k);
// 真正请求起来的时候,我们只需要在set里找即可
if (ans.find(k) != ans.end()) {
puts("Yes");
}else {
puts("No");
}
}
}
return 0;
}
在线变离线是竞赛题当中非常常用的技巧,经常被用来解决一些查询量非常大的问题。说穿了其实并不难,但是如果不知道想要凭自己干想出来则有些麻烦。大家有时间,最好自己亲自用代码实现体会一下。
今天的算法题就聊到这里,衷心祝愿大家每天都有所收获。如果还喜欢今天的内容的话,请来一个三连支持吧~(点赞、关注、转发)
codeforces 1461D,离线查询是什么神仙方法,为什么快这么多?的更多相关文章
- Hibernate的几种查询方式-HQL,QBC,QBE,离线查询,复合查询,分页查询
HQL查询方式 这一种我最常用,也是最喜欢用的,因为它写起来灵活直观,而且与所熟悉的SQL的语法差不太多.条件查询.分页查询.连接查询.嵌套查询,写起来与SQL语法基本一致,唯一不同的就是把表名换成了 ...
- hibernate离线查询DetachedCriteria清除上次的查询条件
1 原例概述 别名重复问题之后,我们还需要解决的问题就是: 如何清除hibernate的上次查询条件,如果不清除,将会导致上次的查询条件和下次的查询条件合并到了一起. 上次的查询条件和本次的查询条件合 ...
- Hibernate——离线查询
1.Criteria查询方式: (1)一般方式: 缺点:每一次查询dao层都需要书写对应的方法,离线查询可以解决这个问题. (2)离线方式: 2.离线查询 用DetachedCriteria来构造查询 ...
- C# 数据操作系列 - 3. ADO.NET 离线查询
0. 前言 在上一篇中,我故意留下了查询的示范没讲.虽然说可以通过以下代码获取一个DataReader: IDataReader reader = command.ExecuteReader(); 然 ...
- ASP.NET MVC 5 - 查询Details和Delete方法
在这部分教程中,接下来我们将讨论自动生成的Details和Delete方法. 查询Details和Delete方法 打开Movie控制器并查看Details方法. public ActionResul ...
- 《Entity Framework 6 Recipes》中文翻译系列 (26) ------ 第五章 加载实体和导航属性之延缓加载关联实体和在别的LINQ查询操作中使用Include()方法
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-7 在别的LINQ查询操作中使用Include()方法 问题 你有一个LINQ ...
- 【SQL】Oracle分页查询的三种方法
[SQL]Oracle分页查询的三种方法 采用伪列 rownum 查询前10条记录 ? 1 2 3 4 5 6 7 8 9 10 11 [sql] select * from t_user t whe ...
- Thinkphp中的volist标签(查询数据集(select方法)的结果输出)用法简介
参考网址:http://camnpr.com/archives/1515.html 通常volist标签多用于查询数据集(select方法)的结果输出,通常模型的select方法返回的结果是一个二维数 ...
- MVC 5 - 查询Details和Delete方法
MVC 5 - 查询Details和Delete方法 在这部分教程中,接下来我们将讨论自动生成的Details和Delete方法. 查询Details和Delete方法 打开Movie控制器并查看De ...
随机推荐
- TCP超时重传时间的选择
一---导读 TCP超时重传时间的选择是计算机网络中较复杂的问题之一,但幸好前辈们都把路铺好了,我们只需要学习并且遵循这些规则,有能力的话去进一步改正. 二---必知的一些专业术语 A--RTT( r ...
- Git之pull,fetch差别
简言之, pull=fetch+merge,下拉远程分支并与本地分支合并. fetch只是下拉远程分支,怎么合并,可以自己再做选择. 进一步了解是,git本地有暂存区(亦称为Index区) fetch ...
- 讲讲Java8的Optional类
前言 Java 8中引入了 Optional 类来解决 NullPointerException 与繁琐的 null 检查,该类首次出现在 Guava.Java 8 才成为类库中的一部分. 入门 Op ...
- 【C++】《C++ Primer 》第十章
第十章 泛型算法 一.概述 因为它们实现共同的操作,所以称之为"算法".而"泛型",指的是它们可以操作在多种容器类型上. 泛型算法并不直接操作容器,而是遍历由两 ...
- 【项目实践】手把手带你搞定SSM
以项目驱动学习,以实践检验真知 前言 现在使用Java后端开发使用的技术栈基本上比较统一:Spring + SpringMVC + Mybatis,即大家常说的SSM.虽然现在流行的做法是使用Spri ...
- 【Spring Boot】创建一个简单的Spring Boot的 Demo
走进Spring Boot 文章目录 走进Spring Boot 环境搭建 新建Spring Boot项目 开始创建项目 配置JDK版本 和 Initializr Service URL 配置Proj ...
- 【Oracle】win7安装报错
在WIN7上安装oracle 10g时,提示如下信息: 正在检查操作系统要求... 要求的结果: 5.0,5.1,5.2,6.0 之一 实际结果: 6.1 检查完成.此次检查的总体结果为: 失败 &l ...
- ctfhub技能树—信息泄露—git泄露—Log
什么是git泄露? 当前大量开发人员使用git进行版本控制,对站点自动部署.如果配置不当,可能会将.git文件夹直接部署到线上环境.这就引起了git泄露漏洞. 打开靶机环境 查看网页内容 使用dirs ...
- 【工具篇】Mysql的安装和使用
[导读]Mysql是数据分析师入门级的技能之一,对于很多小白同学来说,可能还没有机会接触SQL知识.那么我们如何熟悉和练习SQL呢,今天教大家安装两个软件:MySQL和Navicat.后续我们会推出S ...
- Django orm中related_name/related_query_name区别
related_name/related_query_name区别 class Department(models.Model): title = models.CharField(verbose_n ...