技巧专题2(离散化、分块、数列差分化及前缀和)

离散化

每个元素范围很大但元素个数较少的情况。

条件:与数字之间的相对大小有关,而与具体是多少无直接联系。离线。

常见的应用是离散后放到数据结构里。

感觉全是数据结构题。。。(划掉,当然还有计算几何、分块一类的)

 

Line Painting

一个0~1e9的区间,初始都是白的,现进行N次操作,每次将一段区间图上一种颜色。最后求连续最长的白色区间。

 

Mayor's posters

n(n<=10000)个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000)。求出最后还能看见多少张海报。

 

程序自动分析

在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。

考虑一个约束满足问题的简化版本:假设x1,x2,x3...代表程序中出现的变量,给定n个形如xi=xj或xi≠xj的变量相等/不等的约束条件,请判定是否可以分别为每一个变量赋予恰当的值,使得上述所有约束条件同时被满足。例如,一个问题中的约束条件为:x1=x2,x2=x3,x3=x4,x4≠x1,这些约束条件显然是不可能同时被满足的,因此这个问题应判定为不可被满足。

现在给出一些约束满足问题,请分别对它们进行判定。

对于每个问题,包含若干行:
第1行包含1个正整数n,表示该问题中需要被满足的约束条件个数。
接下来n行,每行包括3个整数i,j,e,描述1个相等/不等的约束条件,相邻整数之间用单个空格隔开。若e=1,则该约束条件为xi=xj;若e=0,则该约束条件为xi≠xj。

$N \leq 1e5 , i,j \leq 1e9$

 

Ultra-QuickSort

给一些(n个)乱序的数,让你求冒泡排序需要交换数的次数

$ n \leq 500000 , a_i \leq 1e9 $

 

图形面积

桌面上放了N个平行于坐标轴的矩形,这N个矩形可能有互相覆盖的部分,求它们组成的图形的面积。

$N \leq 100 , |Xi|,|Yi| \leq 1e8 $

将所有的横线和竖线离散化排序,用它们将原矩形切分成一个个不重叠的小矩形,然后累计面积

 

分块

分块,就是将原序列处理成各个小块,目的是尽量地达到处理和询问之间的平衡

常用于:原本O(1)修改O(n)查询或O(n)修改O(1)查询优化成O(sqrt(n))修改O(sqrt(n))查询,或者看起来很像线段树(树状数组)但是又不好维护的。

有些专门卡空间的题可以用分块做(lca)。

能用分块就尽量不用树套树。

分块是一种思想,不一定按照序列分块,也可以按照值域分块、按照大小分块等。

 

教主的魔法

每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[LR](1≤LRN)内的英雄的身高全部加上一个整数W

CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。

$N \leq 1e6 , Q \leq 3000$

保证每块是排好序的,查找整块直接二分查找,然后散块暴力修改。
add标记,这样排好序之后查找的话就可以通过二分跳跃很大一部分查找

 

维护队列

小朋友A有一些弹珠,A喜欢把它们排成队列,从左到右编号为1到N。

为了整个队列鲜艳美观,小朋友想知道某一段连续弹珠中,不同颜色的弹珠有多少。

当然,A有时候会依据个人喜好,替换队列中某个弹珠的颜色。但是A还没有学过编程,且觉得头脑风暴太浪费脑力了,所以向你来寻求帮助。

$N,M \leq 1e4$

对于每一个弹珠维护上一个弹珠的位置pre,分块,对每一块内pre排序。

 

hzwer的分块入门题5

给出一个长为n的数列,以及m个操作,操作涉及区间开方,区间求和。

  

hzwer的分块入门题8

给出一个长为n的数列,以及m个操作,操作涉及区间询问等于一个数c的元素,并将这个区间的所有元素改为c。

 

hzwer的分块入门题9

给出一个长为n的数列,以及m个操作,操作涉及询问区间的最小众数。

陈立杰区间众数解题报告:http://www.docin.com/p-679227660.html

大致思路:

如果不带修改:

1、定理  $mode(A \cup B) \in mode(A) \cup B$

2、基础算法

将数列分成sqrt(n)个块,预处理f[i][j]为从第i个块到第j个块的众数

对于询问的区间[l,r],如果l属于第a块,r属于第b块,a!=b。

那么把[l,r]分成3部分,1:l~a块最后一个; 2:a+1块~b-1块; 3:b块第一个~r

第2部分已经预先处理好,我们需要对 (第2部分的众数+第1部分所有数+第3部分所有数) 中的数查询在[l,r]出现了多少次(两种方法)。

每次询问复杂度sqrt(n)*logn,总复杂度(n+q)*sqrt(n)*logn。

3、优化

期望去掉log,查询一个数在[l,r]中出现次数O(1)

预处理C[i][x]表示前i个块中x出现多少次

预处理A[b][i][x]表示b块的前i个数中x出现多少次

因为一个块中不同的数最多为size=sqrt(n)个,所以C、A的空间复杂度都是n*sqrt(n)的。

对于A的处理我们还需要对每个块开一个表记录在这个块中每个数的id。

如果带修改:

有时间复杂度为 $ O ( (n+q) \times n^{ \frac{2}{3} } ) $的算法。

我们把序列分成$n^{\frac{1}{3}}$块。同样维护每对块之间的结果。

对于每对块之间,我们维护每个值出现了多少次和最大出现次数,同时记录出现了i次的值有多少个。

考虑询问,方法几乎不变,仍是前面预处理的办法,修改只会对C、A中sqrt(n)个数有影响。

 

Pig

红学姐和黄学长是好朋友。有一天,黄学长想吃猪肉丸,于是他去找红学姐买猪。红学姐到她的猪圈中赶猪的时候发现有许多猪逃离了她的猪圈。

同时红学姐发现,一个名叫wwf的魔法猪藏在某个猪圈中施法。

为了确定wwf的位置,方老师向红学姐提出了m组询问,每次询问标号在区间[l,r]内的猪圈剩余的猪的数量和。

猪圈中猪的数量可能是负数。

空间限制:3M

$n,m \leq 500000,|x[i]|  \leq 8000000$

 

Solar Panels

给定A,B,C,D,求满足A≤x≤B&&C≤y≤D的gcd(x,y)的最大值

#include<iostream>
#include<cstdio>
using namespace std;
void doit()
{
    int a,b,c,d;
    scanf("%d%d%d%d",&a,&b,&c,&d);
    a--;c--;
    int i,j,x,y,ans;
    int aa,bb,cc,dd;
    if(d>b) swap(b,d),swap(a,c);
    for(i=1;i<=d;i=j+1)
    {
        j=min(b/(b/i),d/(d/i));
        if(i<=a) j=min(j,a/(a/i));
        if(i<=c) j=min(j,c/(c/i));
        if(b/i>a/i&&d/i>c/i) ans=j;
    }
    printf("%d\n",ans);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--) doit();
}

 

 

[Hnoi2016]最小公倍数

给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值。所有权值都可以分解成2^a*3^b的形式。

现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公倍数为2^a*3^b。

注意:路径可以不是简单路径。

$n,q \leq 50000 , m \leq 100000$

把边按照a每sqrt(m)分一组,然后把询问按b排序,
把在这组及以前的边按b排序把这些边用并查集一条一条插入并维护。
零散的部分暴力插入并记录,做完后暴力撤销注意:并查集不能路径压缩,否则无法撤销回去

 

loj6046爷

给你一个 n 个点的有根树,1 为根,带边权,有 m 次操作。

1 求 x 的子树中第 k 小的深度的值,如果子树中没有 k 个点则输出 -1;
2 将 x 与 x 父亲的边权加上 k。
保证每次操作 2 的 k 以及原树的边权小于等于一个数 len。

如果操作 2 中 x 为 1 ,那么视为将 x 的基础深度加上了 k 。

分块,每隔一段时间重新暴力分块,如果块大小>size或者块内极小值极大值差>S,就重分一块。
//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<set>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(register int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
const int maxn=2e5+7,maxt=1000+7,maxs=2e4+7,S=2e3,INF=0x3f3f3f3f;
int n,m,len,a[maxn],bel[maxn],sz;
int L[maxn],R[maxn];

char cc;ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

int fir[maxn],nxt[maxn],to[maxn],e=0,v[maxn];
void add(int x,int y,int z) {
	to[++e]=y;nxt[e]=fir[x];fir[x]=e;v[e]=z;
}

int dfn[maxn],fed[maxn],dfn_clock;
void dfs(int pos,int d) {
	dfn[pos]=++dfn_clock;
	a[dfn_clock]=d;
	for(int y=fir[pos];y;y=nxt[y]) dfs(to[y],d+v[y]);
	fed[pos]=dfn_clock;
}

int sum[maxt][maxs],pl[maxt],pr[maxt],tot;
int d[maxn];

inline void pd(int x) {
	if(!d[x]) return;
	For(i,L[x],R[x]) a[i]+=d[x];
	d[x]=0;
}

inline void ud(int x) {
	pl[x]=INF; pr[x]=-INF;
	For(j,L[x],R[x]) {
		pl[x]=min(pl[x],a[j]);
		pr[x]=max(pr[x],a[j]);
	}
	For(j,0,pr[x]-pl[x]) sum[x][j]=0;
	For(j,L[x],R[x]) ++sum[x][a[j]-pl[x]];
	For(j,1,pr[x]-pl[x]) sum[x][j]+=sum[x][j-1];
}

void bld() {
	For(i,1,tot) pd(i);
	int now=1,ld=INF,rd=-INF; L[1]=1;
	For(i,1,n) {
		ld=min(ld,a[i]); rd=max(rd,a[i]);
		if(rd-ld>S||i-L[now]>=sz) {
			R[now]=i-1;
			L[++now]=i;
			ld=rd=a[i];
		}
		R[bel[i]=now]=i;
	}
	tot=now;
	For(i,1,tot) ud(i);
}

inline int get_sum(int p,int x) {
	if(x<pl[p]) return 0;
	if(x>pr[p]) return sum[p][pr[p]-pl[p]];
	return sum[p][x-pl[p]];
}

inline int q(int ld,int rd,int x) {
	int rs=0,lt=bel[ld],rt=bel[rd];
	if(lt==rt) {
		For(i,ld,rd) if(a[i]<=x) ++rs;
		return rs;
	}
	if(ld!=L[lt]) {For(i,ld,R[lt]) if(a[i]<=x) ++rs;}
	else rs+=get_sum(lt,x);
	if(rd!=R[rt]) {For(i,L[rt],rd) if(a[i]<=x) ++rs;}
	else rs+=get_sum(rt,x);
	For(i,lt+1,rt-1) rs+=get_sum(i,x);
	return rs;
}

inline int Yth(int ld,int rd,int k) {
	int lt=bel[ld],rt=bel[rd];
	pd(lt); pd(rt);
	if(rd-ld+1<k) return -1;
	int l=INF,r=-INF,mid;
	For(i,lt,rt) l=min(l,pl[i]),r=max(r,pr[i]);
	if(l==r) return l; --l;
	while(l<r-1) {
		mid=(l+r)>>1;
		if(q(ld,rd,mid)>=k) r=mid;
		else l=mid;
	}
	return r;
}

inline void chge(int ld,int rd,int x) {
	int lt=bel[ld],rt=bel[rd];
	if(lt==rt) {
		pd(lt); For(i,ld,rd) a[i]+=x; ud(lt);
		return;
	}
	if(ld!=L[lt]) {
		pd(lt); For(i,ld,R[lt]) a[i]+=x; ud(lt);
	}
	else d[lt]+=x,pl[lt]+=x,pr[lt]+=x;
	if(rd!=R[rt]) {
		pd(rt); For(i,L[rt],rd) a[i]+=x; ud(rt);
	}
	else d[rt]+=x,pl[rt]+=x,pr[rt]+=x;
	For(i,lt+1,rt-1) d[i]+=x,pl[i]+=x,pr[i]+=x;
}

int main() {
	read(n); read(m); read(len); sz=300;
	int op,x,y;
	For(i,2,n) {
		read(x); read(y);
		add(x,i,y);
	}
	dfs(1,0);
	bld();
	For(i,1,m) {
		read(op); read(x); read(y);
		if(op==1) printf("%d\n",Yth(dfn[x],fed[x],y));
		else chge(dfn[x],fed[x],y);
		if(i%1000==0) bld();
	}
	return 0;
}

  

作诗(zuosi)

N个数,M组询问,每次问[l,r]中有多少个数出现正偶数次。

$n,m,c \leq 1e5$

预处理F[i][j]表示第i块到第j块的答案。
一个询问l-r,那么中间大块x-y的答案已经得到了只要考虑l-x和y-r对答案的影响,
对于这里面每个数统计它在x-y出现次数t1,以及l-r出现次数t2,
根据t1,t2的奇偶性考虑其对答案的影响
每块大小sqrt(n/logn),复杂度n sqrt(n logn)

  

莫队

适用范围:离线、查询较多、从[l,r]转移到[l-1,r]或[l+1,r]或[l,r-1]或[l,r+1]比较快(一般为O(1)或O(logn))。

一般分为3种:普通莫队、带修莫队、树上莫队(wjx讲)。

优雅的暴力。

由于只维护当前状态的信息,所以空间也比较小,一般不需要维护一大堆前缀信息或者各种区间信息乱搞。

把每个询问看作是二维平面上的点,那么我们的最小总时间,就是这些点的最小曼哈顿距离生成树, 按照这个树的顺序做,复杂度O(n*sqrt(n))

但是为了简便一般直接分块。

假如有2维,按照第一维分块,每块里面按照另一维排序。这个时候一般每块大小sqrt(n)较优。

假如有3维(带修改,加上时间这一维),每块里面将第二维作为第1关键字,把第三维作为第2关键字排序。这时候一般每块大小$n^{ \frac{2}{3} }$较优。

块的最优大小根据数据的特点而改变(这就叫玄学)。

 

sb题1:给出一个长为n的数列,以及m个操作,操作涉及询问区间的最小众数。不强制在线。

sb题2:n个数,m次询问,每次问[l,r]区间有多少个数恰好出现正偶数次。不强制在线。

 

Mato的文件管理

Mato同学从各路神犇以各种方式收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号。

Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料。他总是从文件大小从小到大看资料。

他先把要看的文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序。

排序程序可以在1单位时间内交换2个相邻的文件(因为加密需要,不能随机访问)。

Mato想要使文件交换次数最小,你能告诉他每天需要交换多少次吗?

$n,q \leq 5e4$

 

XOR and Favorite Number

给你一个长度为n的数列和m个询问,问区间中多少连续子区间满足异或和为k。

$n,m \leq 1e5 , a[x] \leq 1e6$

 

回滚莫队:用于处理难以删除但是易于添加(其实易于删除难以添加也可以)的莫队,排序照常,

如果左右端点在同一块直接暴力,这部分最多n sqrt n,

否则把左端点在一块的一起处理,清空莫队,然后直接令莫队左端点在块尾,这部分n sqrtn,

右端点照常走,这部分n sqrtn ,左端点每次走的时候记录更改了哪些量,

走到地方记录完答案把修改回滚回去,这部分也是n sqrtn.

 

历史研究

有一个长度为N的序列。 
有Q个询问,每次询问l~r范围内每个数值乘以该数值出现次数的最大值。

$N \leq 1e5 , Q \leq 1e5 , X_i \leq 1e9 $

 

数列差分化及前缀和

 

树状数组区间查询区间修改

 

金发姑娘和N头牛

第i头奶牛必须在指定的温度范围内A(i)..B(i)才感觉舒适(0<=A(i)<=B(i)<= 1,000,000,000)。

如果金发姑娘在谷仓放置一个温控器;如果温度T<A(i),牛会太冷,并将产生x单位牛奶。

如果她把恒温器调到(A(i)<=T<=B(i))这个范围内,那么牛会感到舒适,并将产生Y单位牛奶。

如果她把恒温器调到温度T>B(i),牛会感觉很热,并将产生的Z单位牛奶。

正如预期的那样,Y的值总是大于X和Z。给定的X,Y,和Z,以及每个牛的温度的最佳范围,如果金发姑娘设置谷仓的温控器最佳,请计算金发姑娘得到牛奶的最大数量。

已知X,Y和Z都是整数,范围0..1000。温控器可以设置为任意整数的值。

$N \leq 2e4$

把三个区间进行差分。把A、B离散、排序。就把问题转化成了最大前缀和问题。

 

借教室

我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。

共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借dj个教室。

我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。

如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。

现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单 。

$n,m \leq 1e6$

 

中位数图

给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。

$n \leq 1e5$

 

Balanced Lineup

Farmer John 决定给他的奶牛们照一张合影,他让 N (1 ≤ N ≤ 50,000) 头奶牛站成一条直线,每头牛都有它的坐标(范围: 0..1,000,000,000)和种族(0或1)。

他只给一部分牛照相,并且这一组牛的阵容必须是“平衡的”。平衡的阵容,指的是在一组牛中,种族0和种族1的牛的数量相等。

请算出最广阔的区间,使这个区间内的牛阵容平衡。区间的大小为区间内最右边的牛的坐标减去最做边的牛的坐标。

保证每个种族至少有一头牛,没有两头牛的坐标相同。

 

JOIOJI

给一个只由JOI三个字母组成的串,求最长的一个子串使其中JOI三个字母出现次数相等。

$len \leq 2e5$

 

树上差分

将路径上的所有点权值加一,求最后点的权值。找被所有路径共同覆盖的边。

似乎noip很喜欢考。

 

天天爱跑步?

 

运输计划

L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球。

小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。

显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰。

为了鼓励科技创新,L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小 P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。

在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后, 这 m 个运输计划会同时开始,所有飞船一起出发。

当这 m 个运输计划都完成时,小 P 的 物流公司的阶段性工作就完成了。

如果小 P 可以自由选择将哪一条航道改造成虫洞,试求出小 P 的物流公司完成阶段 性工作所需要的最短时间是多少?

$n \leq 3e5$

 

大都市

乡下有依次编号为1..n的n个小村庄,某些村庄之间有一些双向的土路,这些土路构成了一棵树。

Blue Mary从比特堡(根)出发,需要去某个村庄,并且在两次送信经历的间隔期间,有某些土路被改造成了公路.

请你计算出每次送信她需要走过的土路数目。

A a b(1 <= a < b <= n),表示将a到b的土路修为公路

W a, 则表示Blue Mary从比特堡送信到村庄a。

保证父亲一定比儿子编号小。

$n \leq 250000$

 

部落冲突

 

有n个部落他们连城一棵树。处理下面三件事,所有的事件都是按照时间顺序给出的。

1.(Q p q)从第 p 个部落出发的建筑工人想知道能否到达第 q 个部落了,你要回答的便是(Yes/No)
2.(C p q)第 p 个部落与第 q 个部落开战了,保证他们一定是相邻的部落
3.(U x ) 第 x 次发生的战争结束了,它将永远的被载入史册,不复存在

$n \leq 3e5$

 

 

 

参考资料:

https://www.cnblogs.com/SilverNebula/p/6035335.html

http://blog.csdn.net/keshuai19940722/article/details/40410293

http://blog.csdn.net/non_cease/article/details/7383736

http://www.cnblogs.com/BeyondW/p/5908139.html

http://hzwer.com/8053.html

http://hzwer.com/2793.html

http://blog.csdn.net/thy_asdf/article/details/51203421

http://hzwer.com/3663.html

http://www.docin.com/p-679227660.html

http://blog.csdn.net/hzj1054689699/article/details/51866615

https://www.cnblogs.com/Rivendell/p/4141406.html

http://blog.csdn.net/mc_dl/article/details/78408758

http://blog.csdn.net/MaverickFW/article/details/72988286

转载于:https://www.cnblogs.com/Serene-shixinyi/p/7986806.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值