小学期的第一天
上午,困走Linux的迷宫,熟悉了一遍终端,然而什么也没有记住
下午,恶补Java大作业,听教主吹水,深有感触,摇摆于强上大作业与缓学框架之间
晚上,放弃治疗,回归线段树怀抱
夜半,与嵩神交流,单方面碾压,任重而道远
对着教程呼啦呼啦的乱敲,没有章法,只为熟悉,具体的收获谈不上,只能说会用一些奇奇怪怪的编辑器了?然后,用起Ubuntu来不再同以前那般慌张了
犹记得初识PC^2时,小鹿乱撞,好似初恋,想上而不敢
假装学习一下午,开启1w8培训班之路
找到了对口的Java Web实训,然而被前提条件吓到,返回怒补各种框架,从此堕入知识黑洞
期间教主到访,办事间隙吹水赞叹,五年前大弟子发展迅速,超出想象,此番归来惊喜不断
其间更有,我已经培养出这些大弟子,够本了,只要他们隔三差五回来就好。虽为笑语,然亦可从中瞥见教主之得意
这个下午谈了很多,从各自公司变换,Team组成到回来对培养计划的意见,再到我个人的发展,天马行空。
换来换去,选来选去,终于费尽了精力,磨尽了精神。想起来还有个女神等着我约会,赶忙撂下键盘,奔赴现场。然而…这个女神有一点点复杂,懒癌晚期,弃坑而走。本着时刻假装学习的理念,学习了扫描线的写法,发现这只是一个小技巧,并不像原来想象的那样复杂
以我现阶段的认知来说,线段是永远只是线段树,它所能完成只是区间更新/查询,除此之外的任何功能都需要自己来完成,这种扫描线是,上回的二分外壳也是。
因而,线段树本身只完成区间的那部分工作,其他工作由其他部分完成。
队友说,重在建模,有一点模块化封装的意思。
我现在应该是初入线段树了,单点更新/区间更新/扫描线技巧都已经学习,然而对于,线段树的时间复杂度并不能很好的计算,因而做题是只求正确不求时限,超时再说;在构思阶段也无法对具体细节有很好的把握,总是要写到哪儿,编到哪儿,也就是无法把实现落于纸上,造成了编码后面需要大量Debug
现在以POJ 1177 说明存在的问题
对于这道题目,网络上的主流思路在于,离散化,一次扫描更新有扫描线平行的值,于其中更新中断的次数,也就是竖线的条数,完成最终更新
我认为,更新竖线条数的操作过于复杂,需要在segTree当中增加很多的变量,所以才用两次扫描更新,这种写法本应该是快速、简单,然而写孬了。
总共花费 19:00 - 22:30、 23:00 - 00:30、 7:30 - 9: 00
原因在于,建模时,我采用了左闭右闭区间,而这道题目点代表的真的是一个点,而不是一个块(区间?),就造成了,两个段之间的衔接出现了问题
我一直坚持使用CodeBlocks完成这道题目,而CodeBlocks的Debug功能不健全,所以一直没能发现,后面胡乱猜想数据才发现了问题。
教训,在样例出不来的时候,应该尝试简单一些的样例
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <string>
using namespace std;
const int maxn = 5000 + 10;
struct SegTree
{
int l, r, cnt, sum;
}segTree[maxn << 2];
struct Line
{
int a, b, h;
int flag;
Line(int a = 0, int b = 0, int h = 0, int flag = 0)
{
this->a = a, this->b = b, this->h = h, this->flag = flag;
}
bool operator < (const Line & rhs) const
{
return h < rhs.h || h == rhs.h && flag > rhs.flag;
}
}hori_line[maxn << 1], vert_line[maxn << 1];
int cur1 = 0, cur2 = 0;
int new_ab[maxn << 1];
int depress(Line * line)
{
vector<int> pool;
for (int i = 0; i < cur1; i++)
{
pool.push_back(line[i].a);
pool.push_back(line[i].b);
}
sort(pool.begin(), pool.end());
pool.erase(unique(pool.begin(), pool.end()), pool.end());
for (int i = 0; i < cur1; i++)
{
int cur = find(pool.begin(), pool.end(), line[i].a) - pool.begin();
new_ab[cur] = line[i].a; line[i].a = cur;
cur = find(pool.begin(), pool.end(), line[i].b) - pool.begin();
new_ab[cur] = line[i].b; line[i].b = cur;
}
return pool.size() - 1;
}
void build(int k, int l, int r)
{
segTree[k].l = l, segTree[k].r = r;
segTree[k].cnt = 0;
segTree[k].sum = 0;
if (l + 1 >= r)
{
return;
}
int m = (l + r) >> 1;
build(k << 1, l, m);
build(k << 1 | 1, m, r);
}
void update(int k, int l, int r, int c)
{
if (l > segTree[k].r || r < segTree[k].l) return;
if (l <= segTree[k].l && segTree[k].r <= r)
{
segTree[k].cnt += c;
if (segTree[k].cnt == 0)
segTree[k].sum = segTree[k << 1].sum + segTree[k << 1 | 1].sum;
else
segTree[k].sum = new_ab[segTree[k].r] - new_ab[segTree[k].l];
return;
}
if (segTree[k].l + 1 == segTree[k].r)
{
if (segTree[k].cnt == 0)
segTree[k].sum = 0;
else segTree[k].sum = new_ab[segTree[k].r] - new_ab[segTree[k].l];
return;
}
update(k << 1, l, r, c);
update(k << 1 | 1, l, r, c);
if (segTree[k].cnt == 0)
segTree[k].sum = segTree[k << 1].sum + segTree[k << 1 | 1].sum;
}
int solve(Line * line)
{
int w = depress(line);
build(1, 0, w);
int res = 0, pre = 0;
for (int i = 0; i < cur1; i++)
{
update(1, line[i].a, line[i].b, line[i].flag);
int tem = segTree[1].sum;
res += abs(pre - tem);
pre = tem;
}
return res;
}
int main()
{
int n; scanf("%d", &n);
for (int i = 0; i < n; i++)
{
int xx1, yy1, xx2, yy2;
scanf("%d%d%d%d", &xx1, &yy1, &xx2, &yy2);
hori_line[cur1++] = Line(xx1, xx2, yy1, 1);
hori_line[cur1++] = Line(xx1, xx2, yy2, -1);
vert_line[cur2++] = Line(yy1, yy2, xx1, 1);
vert_line[cur2++] = Line(yy1, yy2, xx2, -1);
}
sort(hori_line, hori_line + cur1);
sort(vert_line, vert_line + cur2);
int ans = 0;
ans += solve(hori_line);
//cout << ans << "++++++\n";
ans += solve(vert_line);
//cout << ans << "++++++\n";
printf("%d\n", ans);
return 0;
}
代码总体来说是合理的,逻辑简单而清晰
唯一不足之处在于,对sum进行更新,需要对cnt的不同情况进行分类,我没能把它单独放入一个函数中
我认为这个原因在于一开始没能想到这个细节,这个补充都是后面以及Debug的时候补上的,造成了逻辑有一点混乱
教主要我增加深度,正符合我试水操作系统的打算,咨询了一下嵩神,推荐了MIT课程,半夜翻墙出去,了解了大体情况,全英文课程,难度超群,紧张起来了