2011-0012
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
这题是POJ2482的加强版(原题内容极度浪漫=_=)。[[BR]]
原题题意是给定一个W*H的矩形,再给出一些在平面直角坐标系里的星星,每个星星有一个亮度,求用这个矩形去框住一些星星,使得框内的星星亮度之和最大。[[BR]]
坐标范围很大,于是先离散化,对y坐标建立线段树,直接枚举矩形的右边框在哪,然后左边框最左可以去到哪里。就已经确定了。而且这两个指针都是单调滑动的。[[BR]]
然后现在的问题转化为找一个长度为H的区间,里面星星的和最大。假如没有离散化之前,我们完全可以令Sum[i]表示纵坐标从i-H ... i的星星的亮度之和,然后在插入和删除点的时候维护这个Sum[i](可以发现插入一个点y以后Sum[y]..Sum[y+H]都变化相应的值,所以可以用线段树来维护).[[BR]]
现在离散化以后要变换一下,但是本质上也是差不多。[[BR]]
回到本题,三维情况的话,直接枚举其中一维,然后套用上面那个模型就行了。本题猛犸学长很厚道地给了非常小的坐标范围([-1000,1000]),所以不用离散化,直接上线段树就行了。[[BR]]
附上巨搓代码:[[BR]]
{{{
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
using namespace std;
const int N=1005;
const int INF=0x7FFFFFFF;
int ans;
struct SGT{
struct Node{int l,r,Min,delta;}Tr[2048*6];
void build(int p,int l,int r){
Tr[p].l=l; Tr[p].r=r; Tr[p].Min=0; Tr[p].delta=0;
if (l==r) return;
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
void add(int p,int l,int r,int delta){
if (l<=Tr[p].l && Tr[p].r<=r){
Tr[p].delta+=delta;
return;
}
Tr[p*2].delta+=Tr[p].delta;
Tr[p*2+1].delta+=Tr[p].delta;
Tr[p].delta=0;
int mid=(Tr[p].l+Tr[p].r)/2;
if (l<=mid) add(p*2,l,r,delta);
if (r> mid) add(p*2+1,l,r,delta);
Tr[p].Min=min(Tr[p*2].Min+Tr[p*2].delta,Tr[p*2+1].Min+Tr[p*2+1].delta);
}
int Get_Min(){ return Tr[1].Min+Tr[1].delta; }
}Tree;
struct Node_2{ int x,y,s; };
bool cmp2(Node_2 A,Node_2 B){
return A.x<B.x;
}
struct GaoClass{
int n,m,Lx,Ly;
Node_2 P[N];
void add(int x,int y,int s){ n++; P[n].x=x; P[n].y=y; P[n].s=s; }
void checkmin(int &t,int x){ if (x<t) t=x; }
void Insert(int i){ Tree.add(1,P[i].y,min(2000,P[i].y+Ly),P[i].s); }
void Delete(int i){ Tree.add(1,P[i].y,min(2000,P[i].y+Ly),-P[i].s); }
void gao(){
sort(P+1,P+n+1,cmp2);
Tree.build(1,0,2000);
int j=1;
for (int i=1;i<=n;i++){
Insert(i);
while (P[i].x-P[j].x>Lx) Delete(j++);
checkmin(ans,Tree.Get_Min());
}
}
}Gao;
int n;
int La,Lb,Lc;
struct Node{int a,b,c,s;};
vector <Node> P;
Node make_Node(int a,int b,int c,int s){ Node ret; ret.a=a; ret.b=b; ret.c=c; ret.s=s; return ret; }
bool cmp(Node A,Node B){ return A.a<B.a; }
int main(){
while (scanf("%d",&n)!=EOF){
P.clear();
for (int i=0;i<n;i++){
int a,b,c,s;
scanf("%d%d%d%d",&a,&b,&c,&s);
a+=1000; b+=1000; c+=1000;
if (s<0) P.push_back(make_Node(a,b,c,s));
}
n=P.size();
scanf("%d%d%d",&La,&Lb,&Lc);
Gao.Lx=Lb;
Gao.Ly=Lc;
sort(P.begin(),P.end(),cmp);
ans=0;
for (int i=0;i<n;i++){
Gao.n=0;
for (int j=i;j<n && P[j].a-P[i].a<=La;j++) Gao.add(P[j].b,P[j].c,P[j].s);
Gao.gao();
}
if (ans<0) printf("%d\n",ans);
else printf("Error 404, mahou shoujo not found!\n");
}
}
}}}
By edward_mj
这题是POJ2482的加强版(原题内容极度浪漫=_=)。
原题题意是给定一个W*H的矩形,再给出一些在平面直角坐标系里的星星,每个星星有一个亮度,求用这个矩形去框住一些星星,使得框内的星星亮度之和最大。
坐标范围很大,于是先离散化,对y坐标建立线段树,直接枚举矩形的右边框在哪,然后左边框最左可以去到哪里。就已经确定了。而且这两个指针都是单调滑动的。
然后现在的问题转化为找一个长度为H的区间,里面星星的和最大。假如没有离散化之前,我们完全可以令Sum[i]表示纵坐标从i-H ... i的星星的亮度之和,然后在插入和删除点的时候维护这个Sum[i](可以发现插入一个点y以后Sum[y]..Sum[y+H]都变化相应的值,所以可以用线段树来维护).
现在离散化以后要变换一下,但是本质上也是差不多。
回到本题,三维情况的话,直接枚举其中一维,然后套用上面那个模型就行了。本题猛犸学长很厚道地给了非常小的坐标范围([-1000,1000]),所以不用离散化,直接上线段树就行了。
附上巨搓代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
using namespace std;
const int N=1005;
const int INF=0x7FFFFFFF;
int ans;
struct SGT{
struct Node{int l,r,Min,delta;}Tr[2048*6];
void build(int p,int l,int r){
Tr[p].l=l; Tr[p].r=r; Tr[p].Min=0; Tr[p].delta=0;
if (l==r) return;
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
void add(int p,int l,int r,int delta){
if (l<=Tr[p].l && Tr[p].r<=r){
Tr[p].delta+=delta;
return;
}
Tr[p*2].delta+=Tr[p].delta;
Tr[p*2+1].delta+=Tr[p].delta;
Tr[p].delta=0;
int mid=(Tr[p].l+Tr[p].r)/2;
if (l<=mid) add(p*2,l,r,delta);
if (r> mid) add(p*2+1,l,r,delta);
Tr[p].Min=min(Tr[p*2].Min+Tr[p*2].delta,Tr[p*2+1].Min+Tr[p*2+1].delta);
}
int Get_Min(){ return Tr[1].Min+Tr[1].delta; }
}Tree;
struct Node_2{ int x,y,s; };
bool cmp2(Node_2 A,Node_2 B){
return A.x<B.x;
}
struct GaoClass{
int n,m,Lx,Ly;
Node_2 P[N];
void add(int x,int y,int s){ n++; P[n].x=x; P[n].y=y; P[n].s=s; }
void checkmin(int &t,int x){ if (x<t) t=x; }
void Insert(int i){ Tree.add(1,P[i].y,min(2000,P[i].y+Ly),P[i].s); }
void Delete(int i){ Tree.add(1,P[i].y,min(2000,P[i].y+Ly),-P[i].s); }
void gao(){
sort(P+1,P+n+1,cmp2);
Tree.build(1,0,2000);
int j=1;
for (int i=1;i<=n;i++){
Insert(i);
while (P[i].x-P[j].x>Lx) Delete(j++);
checkmin(ans,Tree.Get_Min());
}
}
}Gao;
int n;
int La,Lb,Lc;
struct Node{int a,b,c,s;};
vector <Node> P;
Node make_Node(int a,int b,int c,int s){ Node ret; ret.a=a; ret.b=b; ret.c=c; ret.s=s; return ret; }
bool cmp(Node A,Node B){ return A.a<B.a; }
int main(){
while (scanf("%d",&n)!=EOF){
P.clear();
for (int i=0;i<n;i++){
int a,b,c,s;
scanf("%d%d%d%d",&a,&b,&c,&s);
a+=1000; b+=1000; c+=1000;
if (s<0) P.push_back(make_Node(a,b,c,s));
}
n=P.size();
scanf("%d%d%d",&La,&Lb,&Lc);
Gao.Lx=Lb;
Gao.Ly=Lc;
sort(P.begin(),P.end(),cmp);
ans=0;
for (int i=0;i<n;i++){
Gao.n=0;
for (int j=i;j<n && P[j].a-P[i].a<=La;j++) Gao.add(P[j].b,P[j].c,P[j].s);
Gao.gao();
}
if (ans<0) printf("%d\n",ans);
else printf("Error 404, mahou shoujo not found!\n");
}
}
By edward_mj