poj2482

题意:平面上有许多点,每个点有一个权值。给定一个大小确定的矩形,边与x,y轴平行,平移这个矩形能圈住的点的权值之和最大是多少。

分析:线段树 c++ac g++re

先把题目转化一下,用矩形的中心点来描述这个矩形的位置。并对每个点建立一个矩形中心点的活动范围,即矩形中心点在这个范围内即可覆盖到该点,建立方法就是以每个点为中心画一个与题中矩形大小相等的矩形。现在点变成了矩形,矩形变成了点。我们要求的是找一个位置来放这个点使得它在最多的矩形内部,即该点的矩形重叠层数最多。

这样我们就可以用线段树来解决了,用一条与y轴平行的扫描线,从左到右来扫描这个矩形重叠图。这条扫描线就是线段树中的整条线段区间,在一个时刻这个线段的不同位置被覆盖了不同层数的矩形,对每次扫描线产生变化后(扫入某一矩形或扫出某一矩形后)求现在正在扫的矩形在线段上覆盖的最大层数。

具体做法是按从左到右的顺序,看所有矩形的左边缘和右边缘,遇到左边缘则将该线段加入线段树,遇到右边缘则将该线段从线段树中删除,每次加入或删除后都要求依次最大覆盖层数,来更新答案。

现在遇到的线段树共有如下几种:

1.插入点型

对于这种线段树,通常是向线段树中插入点,即对应一个叶子节点的信息,而线段树中所有节点也都是记录的关于以该点为根的子树中已插入的点的统计信息,询问通常是问线段树中某个区间对叶子节点的统计信息。

2.线覆盖型

对于这种线段树,与第三种有一下共同特点:所有询问和插入操作都是以区间为单位的,每次都是对一个区间进行操作。每个节点通常会用一个变量来记录以它为跟的子树的相关信息,在回溯过程中更新此变量。当操作的区间能完整的覆盖一个节点对应的区间时直接对该节点操作,不再向下操作。

这种线段树还有其独有的特点:当操作一个短线段时,这个短线段在线段树中由上至下运行到自己对应的位置,这个过程中会经过若干个对应长线段的节点,在经过长线段时,要把长线段的节点中的信息移动到其两个直接子节点中,然后再继续向下走。这样可以保证区间信息存储位置的在树中的纵向专一性,即树中节点的直系血亲之间只能有一个点记录信息。

原因如下:在这种线段树的题通常具有以下性质:(1)新插入线段与旧的线段重叠的部分的信息如果纵向分布,不存储在同一个节点中,则在回溯统计子树信息过程很难计算。(2)新插入的线段与旧线段的重叠部分可以只保存新线段信息,这样才能插入过程中完整覆盖一个节点时不用向下操作。

3.线保留型

这种线段树除了与第二种线段树的共同特点外,还有一个重要特性就是即使旧线段与新线段重叠了旧线段中的信息也仍然有意义,或者要求插入的线段必须保持在其插入的位置不向下迭代。

其中保持位置的一种典型情况就是有删除操作。删除并不是随意的删除,每次删除的线段与原来插入的线段相对应,只删除那些曾经插入过的线段。这种通常我们为了删除时候方便,在短线段向下运行经过长线段的过程中不会把长线段向下迭代,因为长线段是要被删除的,如果向下迭代删除时就没有办法对长线段原来的存储节点进行操作,只能对其许许多多的后代节点中的一些(那些被迭代到了的节点)进行操作,复杂度极高。这种线段树的题通常信息纵向分布也是可以回溯计算的。

 

有时候可能还要综合运用第2、3种线段树。

View Code
#include <iostream>
#include
<cstdlib>
#include
<cstring>
#include
<cstdio>
#include
<algorithm>
usingnamespace std;

#define maxn 100005

struct Node
{
int l, r, value, c;
Node
*pleft, *pright;
} tree[maxn
*6];

struct Line
{
double l, r, x;
int value;
bool end;
} line[maxn
*2];

int n, ax, ay;
double dy[maxn *2];
int lcount, ncount, tot;

booloperator< (const Line &a, const Line &b)
{
if (a.x != b.x)
return a.x < b.x;
return a.end;
}

void input()
{
lcount
=0;
tot
=0;
for (int i =0; i < n; i++)
{
int x, y, c;
double xx = ax /2.0-0.1;
double yy = ay /2.0-0.1;
scanf(
"%d%d%d", &x, &y, &c);
line[lcount].l
= y - yy;
line[lcount].r
= y + yy;
line[lcount].x
= x - xx;
line[lcount].value
= c;
line[lcount].end
=false;
dy[tot
++] = line[lcount].l;
dy[tot
++] = line[lcount].r;
lcount
++;

line[lcount].l
= y - yy;
line[lcount].r
= y + yy;
line[lcount].x
= x + xx;
line[lcount].value
= c;
line[lcount].end
=true;
lcount
++;
}
}

void buildtree(Node *proot, int l, int r)
{
proot
->l = l;
proot
->r = r;
proot
->value =0;
proot
->c =0;
if (l == r)
return;
int mid = (l + r) /2;
ncount
++;
proot
->pleft = tree + ncount;
ncount
++;
proot
->pright = tree + ncount;
buildtree(proot
->pleft, l, mid);
buildtree(proot
->pright, mid +1, r);
}

int binarysearch(double a)
{
int l =0;
int r = tot -1;
while (l < r)
{
int mid = (l + r) /2;
if (dy[mid] < a)
l
= mid +1;
else
r
= mid;
}
return l;
}

void add(Node *proot, int l, int r, int c)
{
if (l > proot->r || r < proot->l)
return;
if (l <= proot->l && r >= proot->r)
{
proot
->c += c;
proot
->value += c;
return;
}
add(proot
->pleft, l, r, c);
add(proot
->pright, l, r, c);
proot
->value = max(proot->pleft->value, proot->pright->value) + proot->c;
}

void work()
{
ncount
=0;
buildtree(tree,
0, tot -1);
int ans =0;
for (int i =0; i < lcount; i++)
{
int l = binarysearch(line[i].l);
int r = binarysearch(line[i].r);
if (!line[i].end)
{
add(tree, l, r, line[i].value);
ans
= max(ans, tree[0].value);
}
else
add(tree, l, r,
-line[i].value);
}
printf(
"%d\n", ans);
}

int main()
{
//freopen("t.txt", "r", stdin);
while (scanf("%d%d%d", &n, &ax, &ay) != EOF)
{
memset(tree,
0, sizeof(tree));
input();
sort(line, line
+ lcount);
sort(dy, dy
+ tot);
tot
= unique(dy, dy + tot) - dy;
work();
}
return0;
}
posted @ 2011-09-06 18:56  金海峰  阅读(2687)  评论(0编辑  收藏  举报