中级农民
- 积分
- 126
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2015-4-22
- 最后登录
- 1970-1-1
|
本帖最后由 zhuli19901106 于 2015-7-24 03:24 编辑
说到线段树,想起了POJ上一道线段树入门题我还没做,于是跑去写。
http://poj.org/problem?id=2528
没想到leetcode做久了,已经忘了POJ坑爹的风格:常数优化。
从一开始以为很快就能AC,到后来越写bug越多,各种调优。中途POJ服务器还挂了好几次。到AC的时候,已经半夜三点了(T_T)- // 2528 Accepted 10872K 94MS G++ 3383B 2015-07-24 02:49:48
- #include <algorithm>
- #include <cstdio>
- #include <cstring>
- using namespace std;
- const int N = 100005;
- int s[N];
- int e[N];
- int d1[2 * N];
- int dc1;
- int d[3 * N];
- int dc;
- int n;
- int b[N];
- int ans;
- typedef struct SegmentTreeNode {
- int start, end;
- int tag;
- SegmentTreeNode *left, *right;
-
- SegmentTreeNode(int _start = 0, int _end = 0) {
- start = _start;
- end = _end;
- tag = -1;
- left = right = NULL;
- }
- } STN;
- const int MAX_NODE = 500000;
- STN nodes[MAX_NODE];
- int nc;
- int mymin(int x, int y)
- {
- return x < y ? x : y;
- }
- int mymax(int x, int y)
- {
- return x > y ? x : y;
- }
- STN *buildTree(int start, int end)
- {
- if (start > end) {
- return NULL;
- }
- STN *root = &(nodes[nc++]);
- root->start = start;
- root->end = end;
- if (start == end) {
- return root;
- }
- int mid = start + (end - start >> 1);
- if (mid < end) {
- root->left = buildTree(start, mid);
- }
- if (mid + 1 > start) {
- root->right = buildTree(mid + 1, end);
- }
- return root;
- }
- void addPoster(STN *root, int start, int end, int tag)
- {
- if (start > end) {
- return;
- }
- if (start == root->start && end == root->end) {
- root->tag = tag;
- return;
- }
- int mid = root->start + (root->end - root->start >> 1);
- int oldtag = root->tag;
- root->tag = -1;
- if (oldtag >= 0) {
- addPoster(root, root->start, start - 1, oldtag);
- addPoster(root, end + 1, root->end, oldtag);
- }
-
- addPoster(root->left, start, mymin(mid, end), tag);
- addPoster(root->right, mymax(mid + 1, start), end, tag);
- }
- void countPoster(STN *root)
- {
- if (root == NULL) {
- return;
- }
- if (root->tag >= 0) {
- if (!b[root->tag]) {
- b[root->tag] = 1;
- ++ans;
- }
- return;
- }
- countPoster(root->left);
- countPoster(root->right);
- }
- void discretization()
- {
- dc = 0;
- d[dc++] = d1[0];
- int i;
- for (i = 1; i < dc1; ++i) {
- if (d1[i] - d1[i - 1] > 1) {
- d[dc++] = d1[i - 1] + (d1[i] - d1[i - 1] >> 1);
- }
- d[dc++] = d1[i];
- }
- }
- void clearTree(STN *root)
- {
- if (root == NULL) {
- return;
- }
- clearTree(root->left);
- clearTree(root->right);
- root->left = NULL;
- root->right = NULL;
- root->tag = -1;
- --nc;
- }
- int removeDuplicate(int a[], int n)
- {
- int i, j;
- int n1 = 0;
- i = 0;
- while (i < n) {
- j = i + 1;
- while (j < n && a[i] == a[j]) {
- ++j;
- }
- a[n1++] = a[i];
- i = j;
- }
- return n1;
- }
- int bs(int x)
- {
- int ll = 0;
- int rr = dc - 1;
- int mm;
- while (ll <= rr) {
- mm = ll + (rr - ll >> 1);
- if (x < d[mm]) {
- rr = mm - 1;
- } else if (x > d[mm]) {
- ll = mm + 1;
- } else {
- return mm;
- }
- }
- return -1;
- }
- void solve()
- {
- scanf("%d", &n);
-
- int i;
- dc1 = 0;
- for (i = 0; i < n; ++i) {
- scanf("%d%d", &s[i], &e[i]);
- d1[dc1++] = s[i];
- d1[dc1++] = e[i];
- }
- sort(d1, d1 + dc1);
- dc1 = removeDuplicate(d1, dc1);
-
- discretization();
-
- STN *root = buildTree(0, dc - 1);
- for (i = 0; i < n; ++i) {
- addPoster(root, bs(s[i]), bs(e[i]), i);
- }
-
- ans = 0;
- memset(b, 0, sizeof(b));
- countPoster(root);
- printf("%d\n", ans);
-
- clearTree(root);
- }
- int main()
- {
- int t, ti;
-
- scanf("%d", &t);
- for (ti = 0; ti < t; ++ti) {
- solve();
- }
-
- return 0;
- }
复制代码 思维有多乱,代码就有多烂。我自己都晕了。
也借这题说说C/C++常系数优化的事吧:
1. set和map很好用,但是手写二分比它们要快。
2. sort函数效率很高,自己手写快排没它快。
3. vector的效率和数组差了好几倍。
4. memset比fill要快
5. new东西很慢,用全局数组比逐个动态分配会快很多。
6. 递归效率不一定低,写法是关键。
7. cin比scanf慢10倍。但如果用了ios::sync_with_stdio(false);则不再需要和C的缓冲区保持同步,于是速度几乎一样了。
8. 位运算可以代替2相关的乘除法,速度差别很大。
9. emplace_back比push_back快点,因为少了一次复制。
把上面的优化技巧都用到了,这题就能在100ms内AC,都不用的话就严重超时。然而,这些都跟复杂度没半毛钱关系。复杂度始终是一样的。
这就是《编程珠玑》里提到的“程序调优”。
|
|