题目链接 : http://acm.hdu.edu.cn/showproblem.php?pid=4391

题意 :

给一段区间, 有两种操作

1 : 给 x 到 y 的区间染色为 z

2 : 查询 x 到 y 的区间内颜色z的数目

思路 :

这题的z最大2^31-1, 区间长度最大1e5, 用线段树将颜色离散化之后维护也存不下

所以用分块哈希, 将一个长度为n的区间分为sqrt(n)块分块维护, 每一块中都用map记录某些颜色的个数

分块哈希 :

修改, 查询一段区间, 对于完整覆盖到的区间, 是可以很快进行修改和查询的

而不完整覆盖的区间至多只有两个, 可以暴力修改

创建一个结构体HashBlock, 其中记录本身长度, 覆盖情况flag, 初始化为-1(代表未被完整覆盖)

确定长度为n的区间, 每块长度为len, 故一共分成了 tot = (n - 1) / s + 1 块

特殊的是最后一段长度是 min(n, (tot) * len) - (tot-1) * len

修改一个区间l, r时, 令la = l / len, ra = r / len, 从[la+1, ra]都被完整覆盖可以直接更新

而区间 [l, (l / len + 1) * len] 和 [r * len, r] 这两段可能未被完整覆盖, 需要手动更新

我细节讲这么多就也为了提醒自己, 再细节的说不下去了, 请看代码吧

第一次写分块哈希的代码, 参考了CXlove, 感谢

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map> using namespace std; const int MAXN = 1e5+; struct HashBlock{
int sizee, color;
map<int, int> m;
} block[]; int c[MAXN];
int len;
int n; void Init()
{
len = (int)sqrt(n);
int tot = (n - ) / len + ;
for(int i = ; i < tot; i++) {
block[i].sizee = min(n, (i+) * len) - i * len;
block[i].color = -;
block[i].m.clear();
}
} //如果一个区间要手动更新, 需要将上一次整块更新的颜色先覆盖, 再重新手动更新
//与线段树中pushdown异曲同工
void PushDown(int step)
{
if(block[step].color != -) {
block[step].m.clear();
for(int i = step * len; i < n && i < (step+) * len; i++) {
c[i] = block[step].color;
block[step].m[c[i]]++;
}
block[step].color = -;
}
} void Update(int l, int r, int color)
{
int la = l / len, ra = r / len;
//完整覆盖的区间可直接更新
for(int i = la + ; i < ra; i++) {
block[i].color = color;
}
//暴力更新未被完整覆盖的部分
if(la != ra) {
PushDown(la), PushDown(ra);
for(int i = l; i < (la+) * len; i++) {
block[la].m[c[i]]--, block[la].m[color]++, c[i] = color;
}
for(int i = ra * len; i <= r; i++) {
block[ra].m[c[i]]--, block[ra].m[color]++, c[i] = color;
}
}
else { //是同一段区间
PushDown(la);
for(int i = l; i <=r; i++) {
block[la].m[c[i]]--, block[la].m[color]++, c[i] = color;
}
}
} int Query(int l, int r, int color)
{
int ans = ;
int la = l / len, ra = r / len;
for(int i = la + ; i < ra; i++) {
if(block[i].color == color) ans += block[i].sizee;
else if(block[i].color == - && \
block[i].m.find(color) != block[i].m.end()) {
ans += block[i].m[color];
}
}
if(la != ra) {
PushDown(la), PushDown(ra);
for(int i = l; i < (la+) * len; i++) {
ans += c[i] == color;
}
for(int i = ra * len; i <= r; i++) {
ans += c[i] == color;
}
}
else {
PushDown(la);
for(int i = l; i <=r ; i++) {
ans += c[i] == color;
}
} return ans;
} int main()
{
int q; while(scanf("%d %d", &n, &q) != EOF) {
Init();
for(int i = ; i < n; i++) {
scanf("%d", &c[i]);
block[i/len].m[c[i]]++;
}
while(q--) {
int cmd, l, r, z;
scanf("%d %d %d %d", &cmd, &l, &r, &z);
if(cmd == ) Update(l, r, z);
else printf("%d\n", Query(l, r, z));
}
} return ;
}

HDU 4391 - Paint The Wall - 分块哈希入门的更多相关文章

  1. HDU 4391 Paint The Wall(分块+延迟标记)

    Paint The Wall Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  2. HDU 4391 Paint The Wall(分块的区间维护)

    题意:给出几个操作,把l-r赋值为z,询问l-r有几个z,其中z < INT_MAX 思路:因为z很大,所以很难直接用线段树去维护.这里可以使用分块来解决.我们可以让每个块用map去储存map[ ...

  3. HDU 4391 Paint The Wall 段树(水

    意甲冠军: 特定n多头排列.m操作 以下是各点的颜色 以下m一种操纵: 1 l r col 染色 2 l r col 问间隔col色点 == 通的操作+区间内最大最小颜色数的优化,感觉非常不科学... ...

  4. hdu 1543 Paint the Wall

    http://acm.hdu.edu.cn/showproblem.php?pid=1543 #include <cstdio> #include <cstring> #inc ...

  5. 线段树 扫描线 L - Atlantis HDU - 1542 M - City Horizon POJ - 3277 N - Paint the Wall HDU - 1543

    学习博客推荐——线段树+扫描线(有关扫描线的理解) 我觉得要注意的几点 1 我的模板线段树的叶子节点存的都是 x[L]~x[L+1] 2 如果没有必要这个lazy 标志是可以不下传的 也就省了一个pu ...

  6. HDU 4012 Paint on a Wall(状压+bfs)

    Paint on a Wall Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others) ...

  7. hdu 3669 Cross the Wall(斜率优化DP)

    题目连接:hdu 3669 Cross the Wall 题意: 现在有一面无限大的墙,现在有n个人,每个人都能看成一个矩形,宽是w,高是h,现在这n个人要通过这面墙,现在只能让你挖k个洞,每个洞不能 ...

  8. 【分段哈希】H. Paint the Wall

    https://www.bnuoj.com/v3/contest_show.php?cid=9147#problem/H [题意] 在一个长为H,宽为W的白墙上选一个矩形区域涂颜色,后涂的颜色会覆盖先 ...

  9. HDU - 6394 Tree(树分块+倍增)

    http://acm.hdu.edu.cn/showproblem.php?pid=6394 题意 给出一棵树,然后每个节点有一个权值,代表这个点可以往上面跳多远,问最少需要多少次可以跳出这颗树 分析 ...

随机推荐

  1. Linux shell入门基础(六)

    六.Shell脚本编程详解 将上述五部分的内容,串联起来,增加对Shell的了解 01.shell脚本 shell: # #perl #python #php #jsp 不同的脚本执行不同的文本,执行 ...

  2. cogs 53 多人背包

    /* 要求每个最优 即累加前k优解 注意不用去重 */ #include<iostream> #include<cstdio> #include<cstring> ...

  3. codevs 1036 商务旅行 (倍增LCA)

    /* 在我还不知道LCA之前 暴力跑的SPFA 70分 三个点TLE */ #include<iostream> #include<cstdio> #include<cs ...

  4. Linq101-Aggregate

    using System; using System.Collections.Generic; using System.Linq; namespace Linq101 { class Aggrega ...

  5. 关于uploadify无法起作用,界面没有效果出现

    <link href="<%: Url.Content("~/Res/uploadify/uploadify.css") %>" rel=&q ...

  6. UITabBarController自定义二之xib

    UITabBarController自定义二之xib 新建一个xib文件 在UITabBarController的子类方法viewDidLoad方法中加载xib 1.-(void)viewDidLoa ...

  7. web前端对上传的文件进行类型大小判断的js自定义函数

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. vi文本编辑器

    vi文本编辑器分为3个模式: 命令模式 插入模式 ex模式 在命令模式下我们可以使用一下功能 o 插入新的行 u 撤销 n yy  复制n行 p 粘贴 / 查找 i 进入插入模式 exc到命令模式 e ...

  9. 如何做好Flex与Java交互

    三种flex4与Java顺利通信的方式是: flex与普通java类通信RemoteObject; flex与服务器交互HTTPService; flex与webservice交互WebService ...

  10. Qt信号槽连接在有默认形参下的情况思考

    写下这个给自己备忘,比如函数 ) 你在调用端如论是test(3)或者test(),都可以正确调用到这个函数. 但是,如果放到Qt中的信号槽的话,这个还是值得讲一讲的,不然的话,可能会引起相应的误会. ...