线段树 小结

来源:互联网 发布:unity3d 数据库 编辑:程序博客网 时间:2024/06/05 02:02

把最进线段树小结一下……

以POJ 2482 为例

Stars in Your Window
Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 12485 Accepted: 3425

Description

Fleeting time does not blur my memory of you. Can it really be 4 years since I first saw you? I still remember, vividly, on the beautiful Zhuhai Campus, 4 years ago, from the moment I saw you smile, as you were walking out of the classroom and turned your head back, with the soft sunset glow shining on your rosy cheek, I knew, I knew that I was already drunk on you. Then, after several months’ observation and prying, your grace and your wisdom, your attitude to life and your aspiration for future were all strongly impressed on my memory. You were the glamorous and sunny girl whom I always dream of to share the rest of my life with. Alas, actually you were far beyond my wildest dreams and I had no idea about how to bridge that gulf between you and me. So I schemed nothing but to wait, to wait for an appropriate opportunity. Till now — the arrival of graduation, I realize I am such an idiot that one should create the opportunity and seize it instead of just waiting. 

These days, having parted with friends, roommates and classmates one after another, I still cannot believe the fact that after waving hands, these familiar faces will soon vanish from our life and become no more than a memory. I will move out from school tomorrow. And you are planning to fly far far away, to pursue your future and fulfill your dreams. Perhaps we will not meet each other any more if without fate and luck. So tonight, I was wandering around your dormitory building hoping to meet you there by chance. But contradictorily, your appearance must quicken my heartbeat and my clumsy tongue might be not able to belch out a word. I cannot remember how many times I have passed your dormitory building both in Zhuhai and Guangzhou, and each time aspired to see you appear in the balcony or your silhouette that cast on the window. I cannot remember how many times this idea comes to my mind: call her out to have dinner or at least a conversation. But each time, thinking of your excellence and my commonness, the predominance of timidity over courage drove me leave silently. 

Graduation, means the end of life in university, the end of these glorious, romantic years. Your lovely smile which is my original incentive to work hard and this unrequited love will be both sealed as a memory in the deep of my heart and my mind. Graduation, also means a start of new life, a footprint on the way to bright prospect. I truly hope you will be happy everyday abroad and everything goes well. Meanwhile, I will try to get out from puerility and become more sophisticated. To pursue my own love and happiness here in reality will be my ideal I never desert. 

Farewell, my princess! 

If someday, somewhere, we have a chance to gather, even as gray-haired man and woman, at that time, I hope we can be good friends to share this memory proudly to relight the youthful and joyful emotions. If this chance never comes, I wish I were the stars in the sky and twinkling in your window, to bless you far away, as friends, to accompany you every night, sharing the sweet dreams or going through the nightmares together. 

Here comes the problem: Assume the sky is a flat plane. All the stars lie on it with a location (x, y). for each star, there is a grade ranging from 1 to 100, representing its brightness, where 100 is the brightest and 1 is the weakest. The window is a rectangle whose edges are parallel to the x-axis or y-axis. Your task is to tell where I should put the window in order to maximize the sum of the brightness of the stars within the window. Note, the stars which are right on the edge of the window does not count. The window can be translated but rotation is not allowed. 

Input

There are several test cases in the input. The first line of each case contains 3 integers: n, W, H, indicating the number of stars, the horizontal length and the vertical height of the rectangle-shaped window. Then n lines follow, with 3 integers each: x, y, c, telling the location (x, y) and the brightness of each star. No two stars are on the same point. 

There are at least 1 and at most 10000 stars in the sky. 1<=W,H<=1000000, 0<=x,y<2^31. 

Output

For each test case, output the maximum brightness in a single line.

Sample Input

3 5 41 2 32 3 26 3 13 5 41 2 32 3 25 3 1

Sample Output

56


其实这题不仅用到线段数,还用到扫描线和离散化……真是学习了。


这题麻烦在x,y的范围很大,还要同时考虑两维。


这里的方法是一维(x)用扫描线,一维(y)用离散化。


离散化大致是这样:

假如y的范围是1~10的10次方,但是其实用到的点的个数只有少数几个,我们就不用对整个1~10的10次方的区间建线段树(也开不了这么大数组),假如有10个点,y分别为1,10,1000,300000000,5,45,99999,5,54,100,我们只需把这几个数放到数组real里,从小到大排序,去掉重复的数,得到的数组的元素个数为realCnt,则只要对1~realCnt建树就可以了,要知道第i个元素的实际大小,用real[i]就行了。这样本题最多10000星星,比y的范围2^31就小多了。


扫描线大致是这样:

对于某一颗星星,窗口在x轴上从左往右移动的时候,先不看y轴,设窗口左上角的坐标的x坐标为X,只有X在一定范围(设为l~r)内,都是可以把这颗星星框在里面的,所以将一颗亮度为value的星星分为两颗,一颗的X为l,亮度为value,一颗X为r,亮度为-value。我们把所以星星以x从小到大排序,然后从小到大一颗颗放上去,某颗星星的左边那条线被放上去,说明进入了这颗星星能影响这个区域,右边的边被放上去,说明已经移出了这颗星星能影响的区域。这样就模拟了窗口的从左到右移动。


设窗口宽width,高height,那么星星所能影响的范围,x方向就是x - width + 1到x,  y方向上就是y到y + height - 1。看到有前辈是用x到x + width - 1,其实应该是一样的。

为什么是这样的范围呢,可以这样理解:

一个窗口的宽和高都确定了,我们要确定它的样子,只需要确定它左上角的点就可以了,所以,不看x的时候,窗口的y在(y, y + height - 1)范围内就能把纵坐标等于y的星星框进去;在不看y的时候,窗口x在(x - width + 1, x)范围内,就能把横坐标为x的星星框进去。为什么不是y+1到y+height-1呢,不是上下都不能正好在边框上吗?原因大概是因为窗口的x和y不一定要去整数。


最后求答案的时候,就是在x从左到右的过程中,不断添加和删除星星,求出过程中亮度最大的那个点的亮度。


本题思路大概如此。


线段树的大致格式:

1.线段树节点

struct Node
{
long long l, r;
int max; 
int flag;
}tree[STAR * 8];

线段树一般用一个结构体数据(也看到有前辈用几个数组的),结构体里面都有l和r表示区间的左右端点。然后根据实际情况会有其他变量。

线段树数组的大小一般是区间大小的四倍,本题因为把一个星星拆成了两个了,所以数组大小是星星数量的8倍。


2.build函数——建立线段树

void build(int node, long long l, long long r)
{
tree[node].l = l;
tree[node].r = r;
tree[node].max = 0;
tree[node].flag = 0;
if(l < r)
{
int mid = (l + r) >> 1;
build(node<<1, l, mid);
build(node<<1|1, mid + 1, r);
}
}

该函数都有一个node,表示现在是数组的第几个元素,l和r表示区间左右端点。

建树的过程是,先l到r保存在该节点,然后去中间值mid,在第node*2个节点保存l到mid,也就是l到r的左半区域,第node*2+1保存mid+1到r,也就是右半区域,直到l == r,也就是区间长度为1,不能再分。


3.updata函数——更新线段数

void updata(int node, long long l, long long r, int value)
{
if(l <= real[tree[node].l] && real[tree[node].r] <= r)
{
tree[node].flag += value;
tree[node].max += value;
return;
}
if(tree[node].l < tree[node].r)
{
if(tree[node].flag != 0)
pushDown(node);
int mid = (tree[node].l + tree[node].r) >> 1;
if(r <= real[mid])
updata(node<<1, l, r, value);
else if(l >= real[mid + 1])
updata(node<<1|1, l, r, value);
else
{
updata(node<<1, l, real[mid], value);
updata(node<<1|1, real[mid + 1], r, value);
}
tree[node].max = Max(tree[node<<1].max, tree[node<<1|1].max);
}
}

一般都有l和r,表示要更新的区间的左右端点。

l <= real[tree[node].l] && real[tree[node].r] <= r表示找到了要更新的节点。否则要更新的节点就是当前区间的子区间,于是求出该区间的中点,然后判断要更新的区间是在左子区间要是右子区间,或者都有,然后根据情况更新相应子区间。


4.pushDown和pushBack——懒惰操作和当前节点的更新

pushDown是用于懒惰操作,这也是线段树能节省时间的原因之一,但一个区间整个被更新的时候,可以先不更新其子节点,以一个变量标记一下(这里用的是flag),到了要更新的时候再更新。

void pushDown(int node)
{
tree[node<<1].flag += tree[node].flag;
tree[node<<1].max += tree[node].flag;
tree[node<<1|1].flag += tree[node].flag;
tree[node<<1|1].max += tree[node].flag;
tree[node].flag = 0;
}

pushBack是指子节点更新后,该节点要根据子节点的情况,更新当前节点的一些数据,比如这题的max。

tree[node].max = Max(tree[node<<1].max, tree[node<<1|1].max);

似乎并不是所有的题目都要pushBack和pushDown,具体情况具体分析……


线段树大概分这个几个部分。


然后再说说这题的数据类型,x和y是范围是2^31次方,似乎并不会超过int的范围,但是像上面说的那么做,如果x和y用int储存的话会Wrong Answer,原因大概是因为y + height - 1之类的,因为加了个height,可能就超过范围了,如果不用y到y + height - 1,而用y - height + 1到y,应该可以全部用int也AC的。不过只是我的猜想,我没有去实验了。


要注意一个问题,代码中直接写数字,默认是int型,而不是long long,所以这么写long long x = 0x7fffffff + 2;得到的将是一个负数,因为这里是先把0x7fffffff + 2的结果用int存储,再赋给x,结果当然是错的,其中0x7fffffff 是int的最大值。

#include<iostream>#include<algorithm>#include<cstdio>#define STAR (10000 + 5)#define Max(a,b) (((a) > (b)) ? (a) : (b))using namespace std;struct Star{long long x;long long upY, downY;int value;                    //星星的亮度,每个星星用两条扫描线,value>0表示入线,value<0表示出线}star[STAR * 2];struct Node{long long l, r;int max;                      //区域内亮度最大值int flag;                     //表示这个区域需要加上flag}tree[STAR * 8];int starNum;long long width, height;int starCnt;long long real[STAR * 2];int realCnt;int ans;void build(int node, long long l, long long r){tree[node].l = l;tree[node].r = r;tree[node].max = 0;tree[node].flag = 0;if(l < r){int mid = (l + r) >> 1;build(node<<1, l, mid);build(node<<1|1, mid + 1, r);}}void pushDown(int node){tree[node<<1].flag += tree[node].flag;tree[node<<1].max += tree[node].flag;tree[node<<1|1].flag += tree[node].flag;tree[node<<1|1].max += tree[node].flag;tree[node].flag = 0;}void updata(int node, long long l, long long r, int value){if(l <= real[tree[node].l] && real[tree[node].r] <= r){tree[node].flag += value;tree[node].max += value;return;}if(tree[node].l < tree[node].r){if(tree[node].flag != 0)pushDown(node);int mid = (tree[node].l + tree[node].r) >> 1;if(r <= real[mid])updata(node<<1, l, r, value);else if(l >= real[mid + 1])updata(node<<1|1, l, r, value);else{updata(node<<1, l, real[mid], value);updata(node<<1|1, real[mid + 1], r, value);}tree[node].max = Max(tree[node<<1].max, tree[node<<1|1].max);}}void addStar(long long x, long long y, int value){starCnt++;star[starCnt].x = x;star[starCnt].downY = y;star[starCnt].upY = y + height - 1;star[starCnt].value = value;}bool cmp(Star s1, Star s2){if(s1.x == s2.x)return s1.value > s2.value;return s1.x < s2.x;}int main(){int i;long long x, y;int value;while(~scanf("%d%d%d", &starNum, &width, &height)){starCnt = 0;realCnt = 0;for(i=1; i<=starNum; i++){scanf("%lld%lld%d", &x, &y, &value);addStar(x - width + 1, y, value);addStar(x, y, -value);real[++realCnt] = y;real[++realCnt] = y + height - 1;}sort(star + 1, star + 1 + starCnt, cmp);sort(real + 1, real + 1 + realCnt);realCnt = unique(real + 1, real + 1 + realCnt) - real - 1;build(1, 1, realCnt);ans = 0;for(i=1; i<=starCnt; i++){updata(1, star[i].downY, star[i].upY, star[i].value);ans = Max(ans, tree[1].max);}printf("%d\n", ans);}return 0;}


原创粉丝点击