hdu1542 Atlantis
The input file is terminated by a line containing a single 0. Don’t process it.
Output a blank line after each test case.
这道题我想了很久,终于A了。。题目是求一些矩形合起来的面积,可以用线段树做。首先用结构体s记录所有矩形的上下边,记录边的左端点x2,右端点x3,纵坐标y,还有增量f(增量的意思是扫到上边的时候总区间减去这条边,扫到下边的时候总区间加上这条边,用1,-1表示),因为线段的坐标都是浮点数,所以要离散化。然后再开一个数组pos,记录所有线段的横坐标,排序后,判断是否有重复值,如果有就跳过,这样做是为了待会建树时方便,因为相同的值建树时没有用。然后建立线段树,用l,r维护每条线段的左右端点,cnt维护这条线段被重复覆盖了几次,0代表没有线段覆盖,大于1表示多次覆盖,len 表示这段区间被覆盖区间的实际总长度,这里对应的是真实坐标的值,并不是线段树中的整数。这道题的线段树比较特别,可以用简单dp做,只有这道题适用,不适用其他题,因为这里保证了有下边就一定有相同长度的上边。
还有最重要的一点(我这里卡了半天= =):就是更新的时候r-1,算总长度的时候r+1;
解释:因为建立线段树的时候我们子区间是这样建的mid=(l+r)/2;build(l,mid),build(mid+1,r),因为对于单纯的线段来说,子区间加起来就是整条线段,但是这里不是这样的,因为我们离散化后,mid,mid+1之间也有距离的!所以如果总长度如果由子区间两端加起来其实是pos[r]-pos[mid+1]+pos[mid]-pos[l],少了一段mid~mid+1.这里有两种方法,第一个是以点建树(即叶子节点表示一个点的左边),运用一个技巧,更新的时候r-1,算总长度的时候r+1;第二种方法是以段建树(即叶子节点表示该点到右边一点的线段,左闭右开),方法和以点建树差不多。
举个例子:要更新的是[1,5],那么我们更新[1,4],然后算的时候算[1,3],[3,4],[4,5],这样就把中间的问题解决了。
动手画一下,就懂了。
代码一:以点建树,用了一个简单动规,只适合本题
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<algorithm>
using namespace std;
struct node{
double x2,x3,y;
int f;
}s[800];
double pos[800];
int m;
struct edge{
int l,r,cnt;
double len;
}b[800];
bool cmp(node a,node b){
return a.y<b.y;
}
int find(double x)
{
int mid,l=1,r=m;
while(l<=r)
{
mid=(l+r)/2;
if(pos[mid]>x)r=mid-1;
else if(pos[mid]<x)l=mid+1;
else return mid;
}
if(pos[l]==x)return l;
else return r;
}
void build(int l,int r,int i)
{
int mid;
b[i].l=l;b[i].r=r;b[i].cnt=b[i].len=0;
if(l==r)return;
mid=(l+r)/2;
build(l,mid,i*2);
build(mid+1,r,i*2+1);
}
void getline(int i)
{
if(b[i].cnt)b[i].len=pos[b[i].r+1]-pos[b[i].l];
else if(b[i].l==b[i].r)b[i].len=0; //这里是叶子节点,已经不是一条线段,所以是0
else b[i].len=b[i*2].len+b[i*2+1].len;
}
void update(int l,int r,int add,int i)
{
int mid;
if(b[i].l==l && b[i].r==r){
b[i].cnt+=add;
getline(i);return;
}
mid=(b[i].l+b[i].r)/2;
if(r<=mid)update(l,r,add,i*2);
else if(l>mid)update(l,r,add,i*2+1);
else {
update(l,mid,add,i*2);
update(mid+1,r,add,i*2+1);
}
getline(i);
}
int main()
{
int n,i,j,num,l,r,k=0;
double x2,y2,x3,y3,sum;
while(scanf("%d",&n)!=EOF && n!=0)
{
num=0;sum=0;
for(i=1;i<=n;i++){
scanf("%lf%lf%lf%lf",&x2,&y2,&x3,&y3);
num++;
s[num].x2=x2;s[num].x3=x3;s[num].y=y2;s[num].f=1;pos[num]=x2;
num++;
s[num].x2=x2;s[num].x3=x3;s[num].y=y3;s[num].f=-1;pos[num]=x3;
}
sort(s+1,s+1+num,cmp);
sort(pos+1,pos+1+num);
m=1;
for(i=2;i<=num;i++){
if(pos[i]!=pos[i-1]){
m++;pos[m]=pos[i];
}
}
build(1,m,1);
s[0].y=0;
for(i=1;i<=num;i++){
sum+=b[1].len*(s[i].y-s[i-1].y);
l=find(s[i].x2);
r=find(s[i].x3)-1;
update(l,r,s[i].f,1);
}
k++;
printf("Test case #%d\n",k);
printf("Total explored area: %.2f\n\n",sum);
}
return 0;
}
代码二:以段建树,用了一个简单动规,只适合本题
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<algorithm>
using namespace std;
struct node{
double x2,x3,y;
int f;
}s[800];
double pos[800];
int m;
struct edge{
int l,r,cnt;
double len;
}b[800];
bool cmp(node a,node b){
return a.y<b.y;
}
int find(double x)
{
int mid,l=1,r=m;
while(l<=r)
{
mid=(l+r)/2;
if(pos[mid]>x)r=mid-1;
else if(pos[mid]<x)l=mid+1;
else return mid;
}
if(pos[l]==x)return l;
else return r;
}
void build(int l,int r,int i)
{
int mid;
b[i].l=l;b[i].r=r;b[i].cnt=b[i].len=0;
if(l==r)return;
mid=(l+r)/2;
build(l,mid,i*2);
build(mid+1,r,i*2+1);
}
void getline(int i)
{
if(b[i].cnt)b[i].len=pos[b[i].r+1]-pos[b[i].l];
else if(b[i].l==b[i].r){
b[i].len=0;
}
else b[i].len=b[i*2].len+b[i*2+1].len;
};
void update(int l,int r,int add,int i)
{
int mid;
if(b[i].l==l && b[i].r==r){
b[i].cnt+=add;
getline(i);return;
}
mid=(b[i].l+b[i].r)/2;
if(r<=mid)update(l,r,add,i*2);
else if(l>mid)update(l,r,add,i*2+1);
else {
update(l,mid,add,i*2);
update(mid+1,r,add,i*2+1);
}
getline(i);
}
int main()
{
int n,i,j,num,l,r,k=0;
double x2,y2,x3,y3,sum;
while(scanf("%d",&n)!=EOF && n!=0)
{
num=0;sum=0;
for(i=1;i<=n;i++){
scanf("%lf%lf%lf%lf",&x2,&y2,&x3,&y3);
num++;
s[num].x2=x2;s[num].x3=x3;s[num].y=y2;s[num].f=1;pos[num]=x2;
num++;
s[num].x2=x2;s[num].x3=x3;s[num].y=y3;s[num].f=-1;pos[num]=x3;
}
sort(s+1,s+1+num,cmp);
sort(pos+1,pos+1+num);
m=1;
for(i=2;i<=num;i++){
if(pos[i]!=pos[i-1]){
m++;pos[m]=pos[i];
}
}
build(1,m-1,1);
s[0].y=0;
for(i=1;i<=num;i++){
sum+=b[1].len*(s[i].y-s[i-1].y);
l=find(s[i].x2);
r=find(s[i].x3)-1;
update(l,r,s[i].f,1);
}
k++;
printf("Test case #%d\n",k);
printf("Total explored area: %.2f\n\n",sum);
}
return 0;
}
代码三:适用范围广,可以用于其他增减线段的题
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<algorithm>
using namespace std;
#define maxn 250
struct edge{
double x2,x3,y;
int f;
}a[maxn];
double num,pos[maxn];
int n,m;
bool cmp(edge a,edge b){
return a.y<b.y;
}
struct node{
int l,r,cnt;
double len;
}b[4*maxn];
void build(int l,int r,int i)
{
int mid;
b[i].l=l;b[i].r=r;b[i].cnt=b[i].len=0;
if(l==r)return;
mid=(l+r)/2;
build(l,mid,i*2);
build(mid+1,r,i*2+1);
}
void update(int l,int r,int add,int i)
{
int mid;
if(b[i].cnt!=-1 && b[i].l==l && b[i].r==r){
b[i].cnt+=add;return;
}
if(b[i].cnt!=-1){
b[i*2].cnt=b[i*2+1].cnt=b[i].cnt;
b[i].cnt=-1;
}
mid=(b[i].l+b[i].r)/2;
if(r<=mid)update(l,r,add,i*2);
else if(l>mid)update(l,r,add,i*2+1);
else{
update(l,mid,add,i*2);
update(mid+1,r,add,i*2+1);
}
}
void question(int l,int r,int i)
{
int mid;
if(b[i].l==l && b[i].r==r && b[i].cnt>0){
num+=pos[b[i].r+1]-pos[b[i].l];return;
}
if(b[i].cnt==0)return;
mid=(b[i].l+b[i].r)/2;
if(r<=mid)question(l,r,i*2);
else if(l>mid)question(l,r,i*2+1);
else {
question(l,mid,i*2);
question(mid+1,r,i*2+1);
}
}
int find(int l,int r,double x)
{
int mid;
while(l<=r){
mid=(l+r)/2;
if(pos[mid]==x)return mid;
else if(pos[mid]>x)r=mid-1;
else l=mid+1;
}
if(pos[l]==x)return l;
else return r;
}
int main()
{
int i,j,t,t1,t2,num1=0;
double c,d,e,f,x,y,xx,yy,sum;
while(scanf("%d",&n)!=EOF && n!=0)
{
t=0;
for(i=1;i<=n;i++){
scanf("%lf%lf%lf%lf",&x,&y,&xx,&yy);
pos[i]=x;pos[i+n]=xx;
t++;
a[t].y=y;a[t].x2=x;a[t].x3=xx;a[t].f=1;
t++;
a[t].y=yy;a[t].x2=x;a[t].x3=xx;a[t].f=-1;
}
sort(pos+1,pos+1+2*n);
m=1;
for(i=2;i<=2*n;i++){
if(pos[m]!=pos[i]){
m++;pos[m]=pos[i];
}
}
build(1,m,1);
sum=0;
sort(a+1,a+1+2*n,cmp);
a[0].y=0;
for(i=1;i<=2*n;i++){
num=0;
question(1,m-1,1);
sum+=num*(a[i].y-a[i-1].y);
t1=find(1,m,a[i].x2);
t2=find(1,m,a[i].x3);
update(t1,t2-1,a[i].f,1);
}
num1++;
printf("Test case #%d\n",num1);
printf("Total explored area: %.2f\n",sum);
printf("\n");
}
return 0;
}