题目链接 : 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. Android蓝牙操作笔记(转)

    蓝牙是一种支持设备短距离传输数据的无线技术.android在2.0以后提供了这方面的支持. 从查找蓝牙设备到能够相互通信要经过几个基本步骤(本机做为服务器): 1.设置权限 在manifest中配置 ...

  2. EventBus分析

    1. 功能介绍 1.1 EventBus EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递,这里的事件可以理解为消息,本文中统一称为事 ...

  3. Strust2 <c:forEach> 循环控制标签

    <c:forEach>为循环控制标签 语法:迭代一集合对象中的所有成员 <c:forEach [var="varName"] items="collec ...

  4. python-增删改查

    ###增删改查 names = ["zhangding","wangxu","wudong","cheng"] #增 n ...

  5. (转)ThinkPHP使用心得分享-分页类Page的用法

    转之--http://www.jb51.net/article/50138.htm ThinkPHP中的Page类在ThinkPHP/Extend/Library/ORG/Util/Page.clas ...

  6. 初识Activity

    Callback Description onCreate() This is the first callback and called when the activity is first cre ...

  7. Linux svn一次增加多个文件并批量上传

    命令行下操作svn没有使用界面形式的TortoiseSVN直观,但是不管怎样,命令行下操作svn还是有它的有点,如果你碰到一次需要svn add许多个文件怎么办?下面的命令可以帮助你解决这个问题 一次 ...

  8. Swift - 23 - 选择结构

    //: Playground - noun: a place where people can play import UIKit var rating = "A" // if - ...

  9. NPM环境搭建

    1. NPM全局路径:配置npm包的安装位置,在你的用户文件夹下,新建.npmrc文件:cache=D:\NodeJs\nvm\npm-cache 表示缓存文件夹 prefix=D:\NodeJs\n ...

  10. Eclipse怎么忽略掉报错的js文件

    第一步,我们要先定位错误在哪里,选择菜单里window——show view——other,选择Problems. 第二步,点击有红叉的项目,在Problems视图中,可以看到是什么错,哪个文件夹中的 ...