http://poj.org/problem?id=2482
黑书的102~104有个十分类似的题,解法一样 推荐
题目大意:
刚开始的情书 ,十分的不错。
给你个矩形去框星星 矩形能移不能转,边框上的星星不算,星星都是有亮度值的
问可以得到的最大亮度值和
思路:
由于都是整数,比如说边框左边有星星 右边也有星星 只有左右移动一下就可以多框
一些星星,所以我们默认 边框星星 取左不取右,取下不取上
我们对y坐标排序,根据y建二叉树 左子树一定小于根节点 右子树一定大于根节点
对x排序,我们用两个扫描线,使它们两者之间的星星x距离不超过w
然后对其求最大矩形内亮度和。
我们可以把一个点拆成两个 原来的点是x ,y,value再多加一个点 x,y+h,-value
这样的话 两个扫描线之间的星星求矩形内点亮度和,变成了,用一个扫描线向上扫,求扫过部分的
所用点的两度和。
至于点的更新 和答案的更新 见代码及其注释:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<queue> #include<algorithm> using namespace std; const int N=10010; struct node { int I;//在完全二叉树里的位置 int x; unsigned int y;//y加h后会超过2^31 int value; }mem[N*4]; struct tree { int value; int max; int sum; }btree[N*8]; bool cmpx(node a,node b) { return a.x<b.x; } bool cmpy(node a,node b) { if(a.y==b.y)//要把负的value排在前面 return a.value<b.value; return a.y<b.y; } void build(int l,int r,int w) { int k=(l+r+1)>>1;//区间内的根节点 mem[k].I=w;//记录在完全二叉树里的位置 if(l<k) { build(l,k-1,2*w); } if(k<r) { build(k+1,r,2*w+1); } } void update(int x) { btree[x].sum=btree[x*2].sum+btree[x*2+1].sum+btree[x].value;//sum需要相加 btree[x].max=max(max(btree[x*2].max,0),//max需要用到DP的思想 max(btree[x*2].sum+btree[x].value,btree[x*2].sum+btree[x].value+btree[x*2+1].max)); if(x!=1)//不是总根节点 继续更新 { update(x/2); } } int main() { int n,w,h; while(scanf("%d %d %d",&n,&w,&h)!=EOF) { for(int i=1;i<=n;++i) { scanf("%d %d %d",&mem[i].x,&mem[i].y,&mem[i].value); } for(int i=n+1;i<=2*n;++i)//一个点拆成两个点 { mem[i].x=mem[i-n].x; mem[i].y=mem[i-n].y+h; mem[i].value=-mem[i-n].value; } for(int i=0;i<=4*n+1;++i)//初始化 4*n+1 中的+1很重要 { btree[i].max=0; btree[i].value=0; btree[i].sum=0; } sort(mem+1,mem+2*n+1,cmpy);//按y排序 build(1,2*n,1);//建树 实际上是找好对于的点 sort(mem+1,mem+2*n+1,cmpx);//按x排序 int L,R; L=R=1; int ans=0; while(R<=2*n) { while(R<=2*n&&mem[R].x-mem[L].x<w)//加点 { btree[mem[R].I].value=mem[R].value; update(mem[R].I); ++R; } ans=max(ans,btree[1].max);//加点后求最大值 while(R<=2*n&&mem[R].x-mem[L].x>=w)//去点 { btree[mem[L].I].value=0; update(mem[L].I); ++L; } } printf("%d\n",ans); } return 0; }