将整个区间分为sqrt(n)段小区间,每个段长度为sqrt(n),并维护每一段的统计量,这样可以在sqrt(n)时间内完成一次区间查询或更新。平衡查询和更新之间矛盾是数据结构设计时的重要考虑因素,而这种做法是经典的平衡做法,早就了解,但以前没有遇到过只能用“分段”解决的题目,因此总觉着这是迫不得已的“赖招”,也从来没有写过,昨天第一次写,小细节没注意,结果悲剧的写成每次查询O(n)了。总结一下错误:
1.对于整个区间的更新,不需要实际处理每个元素的值,只需要用一个变量标记一下即可,当整段元素更新量不一致时再更新每个元素,这样才能够保证查询时sqrt(n)的复杂度,否则就O(n)了。。。
2.如果使用map代替哈希表,如果元素不在map中不要直接查询,因为查询不存在的元素会自动插入,空间复杂度会变为n*n。
3.对于最后一段,长度可能与前边的段不同,因此需要特殊处理最后一段的长度。
鉴于java中HashMap效率很客观,自己懒得写哈希表,使用map会增加复杂度,虽然oj支持不好而且不支持下标caoz,最终还是选择了用java搞分段哈希
其它题目:
Spoj3261 Race Against Time修改某个值,查询谋取区间内小于等于k的元素个数
解法:分段+排序/Treap
spoj 861 SWAPS动态维护逆序对
解法:分段/Treap+树状数组(http://blog.csdn.net/kksleric/article/details/7929903)
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.StreamTokenizer; import java.util.HashMap; public class Hash { int maxn = 100010, maxm = 335; class Zone { HashMap<Integer, Integer> mp = new HashMap<Integer, Integer>(); int color[] = new int[maxm], len, all; void init(int ln) { mp.clear(); all = -1; len = ln; } int get(int c) { if (mp.containsKey(c)) return mp.get(c); else return 0; } void set(int c) { all = c; } void update(int left, int right, int c) { if (all == c) return; if (all != -1) { for (int i = 1; i < left; i++) color[i] = all; for (int i = right + 1; i <= len; i++) color[i] = all; } for (int i = left; i <= right; i++) color[i] = c; all = -1; mp.clear(); for (int i = 1; i <= len; i++) mp.put(color[i], get(color[i])+1); } int query(int left, int right, int c) { if (all == -1) { int sum = 0; for (int i = left; i <= right; i++) if (color[i] == c) sum++; return sum; } else { if (all == c) return right - left + 1; else return 0; } } } Zone zz[] = new Zone[maxm]; int belong[] = new int[maxn], id[] = new int[maxn]; int a[] = new int[maxn], M; void build(int n) { for (int i = 1; i * i <= n; i++) M = i; int cnt = 0, len = M; for (int i = 1; i <= n; i++) { if (len == M) { zz[++cnt].init(M); len = 0; } belong[i] = cnt; id[i] = ++len; zz[cnt].color[len] = a[i]; zz[cnt].mp.put(a[i], zz[cnt].get(a[i]) + 1); } zz[cnt].len = len; } void update(int left, int right, int c) { int l = belong[left], r = belong[right]; if (l == r) { zz[r].update(id[left], id[right], c); } else { zz[l].update(id[left], zz[l].len, c); for (int i = l + 1; i < r; i++) zz[i].set(c); zz[r].update(1, id[right], c); } } int query(int left, int right, int c) { int sum = 0; int l = belong[left], r = belong[right]; if (l == r) { sum += zz[l].query(id[left], id[right], c); } else { sum += zz[l].query(id[left], zz[l].len, c); for (int i = l + 1; i < r; i++) { if (zz[i].all == c) sum += zz[i].len; if(zz[i].all==-1) sum += zz[i].get(c); } sum += zz[r].query(1, id[right], c); } return sum; } void run() throws IOException { for (int i = 1; i < maxm; i++) zz[i] = new Zone(); while (in.nextToken() != in.TT_EOF) { int n = (int) in.nval; int m = nextInt(); for (int i = 1; i <= n; i++) a[i] = nextInt(); build(n); int t, a, b, c; while (m-- > 0) { t = nextInt(); a = nextInt() + 1; b = nextInt() + 1; c = nextInt(); if (t == 1) update(a, b, c); else System.out.println(query(a, b, c)); } } } StreamTokenizer in = new StreamTokenizer(new BufferedReader( new InputStreamReader(System.in))); int nextInt() throws IOException { in.nextToken(); return (int) in.nval; } public static void main(String[] args) throws IOException { new Hash().run(); } }