Tag-树状数组

Icontofig 's Blog // 我们的征程是星辰大海!Away From OI,Come to ICPC(查看友链请点About Me)

 2019-05-19 21:03:32 |  0 Comments  |  莫队 树状数组

XTCPC2019 湘潭邀请赛 Problem C Chika and Friendly Pairs (莫队+树状数组)

 

题意:

给定n个数,m条询问,每个询问中包含l、r查询这个区间[l,r]中有多少个数对满足两个数的绝对值之差小于等于K

题解:

上来一看区间查询,第一反应一般都是线段树/树状数组等数据结构。

但是看一下这道题要求什么,求区间内满足条件的数对个数。

我好像没有见过能维护数对个数的树状数组或者线段树什么的。

对于数对而言,一般都应该先枚举其中一个数,剩下的才能去判断吧。

如果两个数i,j满足绝对值之差小于等于K,那么肯定能有这样一个关系式:ik<=j<=i+k

这样的话,对于某个区间内的所有数,能跟当前枚举出来的数x组成满足题意的数对的,一定在[x-k,x+k]这个区间里,我们只需要统计这个区间有多少个数就行了,这样每一侧的操作是O(Llogn)(L代表区间长度)的。

但是现在问的是对于某个给定的区间内的所有数能够组成的合法数对个数,我们就不能对区间每个数都像上面这样子暴力去统计了。那要怎么办呢?

答案就是使用莫队算法,里层查找答案时使用树状数组。

使用莫队算法能够使得我们有效利用重叠区间所统计的答案,而不必过多的修改统计树状数组来查询答案。

莫队的基本做法就是:先√n对区间分块,然后按照左端点所属块的序号为第一关键字,右端点大小为第二关键字对询问操作进行排序,然后设置l和r代表当前的位置指针, 枚举询问,不断移动l和r使之与当前的询问的l和r重合,期间添加修改信息。

对于此题,在l,r移动时,区间每加入一个数,先用树状数组统计当前区间内的满足条件的数的个数,然后将此数插入树状数组中。区间中要移除一个数的时候类似,不过两个步骤要反过来一下。读者可以自行理解一下为什么。

此题还有一个关键点是,离散化数x、x-k,x+k所组成的新数组,否则数太大树状数组会开不下的。然后离散化后的标号要O(n)用数组处理,不能使用map记录(map的查询复杂度是O(logn)的,会超时)。

总体复杂度O(n√nlogn)(实际上达不到这么大好像)

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <map>
using namespace std;
const int maxn = 1e5+5;
map<int,int>u
 2019-02-27 23:53:01 |  0 Comments  |  树状数组

BZOJ 1537 POI2005 AUT-The Bus 二维偏序问题

由于格式问题,这道题的题面就不往这里放了,给一个洛谷的链接吧:

[POI2005]AUT-The Bus

题解

这道题有中文题意,没有BZOJ权限号的可以去洛谷上搜到这道题。
首先会想到DP,但是一看这个数据范围也太大了,离散化后也没法做二维DP。
但是离散化也是要的,不然10的9次方的数据谁顶得住啊。
考虑这是一个二维偏序问题。一般的二维偏序都是找比当前物品的两个值都小的问题,而这道题并不是,这道题要做的是维护最大前缀和。
首先按照y排序并且离散化每个车站的y坐标,然后再按照x排序,使用树状数组维护最大前缀和即可(当然线段树应该也是可以的,只不过会比树状数组麻烦一点)。注意这里的树状数组不是维护的前缀和,而是维护前缀和的最大值。所以update操作和query操作里并不是求和,而是取最大值。


代码

#include <bits/stdc++.h>
using namespace std;
int get_num(){
    int num = 0;
    char c;
    bool flag = false;
    while((c = getchar()) == ' ' || c == '\r' || c == '\n');
    if(c == '-')
        flag = true;
    else num = c - '0';
    while(isdigit(c = getchar()))
        num = num * 10 + c - '0';
    return (flag ? -1 : 1) * num;
}
const int maxn = 1e5+5;
struct point{
    int x,y,k;
}p[maxn];
bool cmp1(point a,point b){
    return a.y < b.y;
}
bool cmp2(point a,point b){
    if(a.x == b.x)return a.y < b.y;
    return a.x < b.x;
}
int lowbit(int x){
    return x & (-x);
}
int c[maxn];
int n,m,k,cnt;
void update(int x,int d){
    while(x <= 
 2017-01-18 08:56:15 |  0 Comments  |  CDQ分治 树状数组

bzoj 2683 简单题【CDQ分治+树状数组】

Description

你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作:
命令 参数限制 内容
1 x y A 1<=x,y<=N,A是正整数 将格子x,y里的数字加上A
2 x1 y1 x2 y2 1<=x1<= x2<=N1<=y1<= y2<=N 输出x1 y1 x2 y2这个矩形内的数字和
3 无 终止程序

Input

输入文件第一行一个正整数N。

接下来每行一个操作。

Output

对于每个2操作,输出一个对应的答案。

Sample Input

4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3

Sample Output

3
5

HINT

1<=N<=500000,操作数不超过200000个,内存限制20M。

对于100%的数据,操作1中的A不超过2000。

题解

首先对于这道题,看了范围之后,二维的数据结构是显然不能过的,于是我们可能会考虑把一维排序之后另一位上数据结构什么的,然而cdq分治却能够很好的体现它的作用。

首先,对于每一个询问求和,显然是x在它左边的并且出现时间在它之前的所有的change对他可能会有影响。

我们按照x第一关键字,y第二关键字,操作第三关键字来排序所有的询问,然后在cdq的时候,每次递归处理左半区间,按照x动态的将y这一列的值加到树状数组里,来更新右半边的所有询问,注意这里的树状数组是需要清的,也就是每次cdq都是采用不同的树状数组。

另:这题神坑,数组开小不是RE而是WA

数组范围是要开到200000*4的,因为对于每个1操作,是一个操作,而2操作根据容斥原理,是4个操作。所以复杂度大概是多少呢O(nlog2(n))cdq+树状数组。n<=800000

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int get_num(){
    int num = 0;
    char c;
    bool flag = false;
    while((c = getchar()) == ' ' || c == '\r' || c == '\n');
    if(c == '-')
      
Title - Artist
0:00