Picture
IOI 1998

A number, N (1 <= N < 5000), of rectangular posters, photographs and other pictures of the same shape are pasted on a wall. Their sides are all vertical or horizontal. Each rectangle can be partially or totally covered by the others. The length of the boundary of the union of all rectangles is called the perimeter. Write a program to calculate the perimeter.

Figure 1 shows an example with seven rectangles: 

Figure 1. A set of seven rectangles

The corresponding boundary is the whole set of line segments drawn in Figure 2: 
 
Figure 2. The boundary of the set of rectangles

The vertices of all rectangles have integer coordinates. All coordinates are in the range [-10000,10000] and any existing rectangle has a positive area. The numeric value of the result fits in a 32-bit signed representation.

PROGRAM NAME: picture

INPUT FORMAT

Line 1: N, the number of rectangles pasted on the wall.
Lines 2..N+1 In each of the subsequent lines, one can find the integer coordinates of the lower left vertex and the upper right vertex of each rectangle. The values of those coordinates are given as ordered pairs consisting of an x-coordinate followed by a y-coordinate.

SAMPLE INPUT (file picture.in)

7
-15 0 5 10
-5 8 20 25
15 -4 24 14
0 -6 16 4
2 15 10 22
30 10 36 20
34 0 40 16

OUTPUT FORMAT

A single line with a non-negative integer which corresponds to the perimeter for the input rectangles.

SAMPLE OUTPUT (file picture.out)

228

———————————————————————————————————————————题解
这道题暴力完全可以做
 /*
ID: ivorysi
LANG: C++
PROG: picture
*/
#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define sigongzi(j,x,y) for(int j=(x);j>(y);--j)
#define inf 0x5f5f5f5f
#define ivorysi
#define mo 97797977
#define hash 974711
#define base 47
#define fi first
#define se second
#define pii pair<int,int>
#define esp 1e-8
typedef long long ll;
using namespace std;
struct node {
int s,t,y,on;
node (int st=, int ed=, int ceng=, int num=) {//要写上初值
s=st;t=ed;y=ceng;on=num;
}
bool operator <(const node &b) const {
return y<b.y || (y==b.y&&on>b.on);
}
}seg[];
int n;
int lx[],rx[],ly[],ry[],ans,level[];
void solve() {
scanf("%d",&n);
siji(i,,n) {
scanf("%d%d%d%d",&lx[i],&ly[i],&rx[i],&ry[i]);
}
siji(i,,n) {
seg[i*-] = node(lx[i],rx[i],ly[i],);
seg[i*] = node(lx[i],rx[i],ry[i],-);
}
sort(seg+,seg+*n+);
siji(i,,*n) {
xiaosiji(j,seg[i].s,seg[i].t) {
if(level[j+]+seg[i].on== && level[j+]==) ++ans;
else if(level[j+]+seg[i].on== && level[j+]==) ++ans;
level[j+]+=seg[i].on;
}
}
siji(i,,n) {
seg[i*-] = node(ly[i],ry[i],lx[i],);
seg[i*] = node(ly[i],ry[i],rx[i],-);
}
sort(seg+,seg+*n+);
memset(level,,sizeof(level));
siji(i,,*n) {
xiaosiji(j,seg[i].s,seg[i].t) {
if(level[j+]+seg[i].on== && level[j+]==) ++ans;
else if(level[j+]+seg[i].on== && level[j+]==) ++ans;
level[j+]+=seg[i].on;
}
}
printf("%d\n",ans);
}
int main(int argc, char const *argv[])
{
#ifdef ivorysi
freopen("picture.in","r",stdin);
freopen("picture.out","w",stdout);
#else
freopen("f1.in","r",stdin);
#endif
solve();
return ;
}

哼,我可不是只是暴力可以做就暴力做的人

然后我用了许多种【奇奇怪怪而且错了的】线段树解法,让我深切的体会到我线段树学的是真不好

那么……

错解1

线段树区间加的模型,然后维护一个cover表示这个区间被覆盖了多少次,每次加上一个flag【如果这是矩形的底边flag=1,如果这是矩形的顶边flag=-1】

更新线段的时候,发现这个区间的cover=0,我们加上这个区间的长度*2

我们对于横向做一遍,对于纵向做一遍

……这,连样例都过不了

为什么呢,经过我gdb孜孜不倦的调试,我发现了原因……

如果这样的话我们需要加上5->9 *2 然而我们更新的话会直接刷掉0->9的cover

然后就有了

错解1.4

我们的区间可以是它左右两个区间的cover取个min嘛……

【然而这样有啥用?】

要不然就和自己的cover与左右两个区间的最小cover取个max……

【……啥】

错解1.47

我们的区间可以是它左右两个区间和嘛……

【你讲得出来道理吗就瞎搞】

错解2

我觉得1系列可能做不出来了

于是想到了2系列,我们记录一个区间被覆盖的长度,ans+最后根的长度的变化值【显得比较有道理了】

然而我是怎么写的……【自己都不忍心看】

维护一个len不断加上压进来的线段

【那样遇到连个重合的底边线段你难道不是随时就原地爆炸吗】

错解2.4

那我再维护一个cover,算这个线段有没有被覆盖,被覆盖就加上这个区间的长度

为啥是0……

哦还要加上左右两边的的区间长度

然后还是不对

错解2.47

怎么就不对呢……然后我翻了翻hzwer神犇矩形面积求并的代码

他好像没有维护lazy啊……

然后经过一番奇奇怪怪的思索

我好像有点理解我自己奇怪的算法为什么错了

【我这个代码不止维护了lazy还加了一句异常奇怪的cover=左右两边区间覆盖取个min】

维护lazy应该是没有什么问题的,但是这就相当于把一个大线段拆小,再复原

后面一句应该是因为这相当与把线段合并成一个大的,也没什么意义

两个一起上就有奇怪的化学反应?

所以正解3

压线段进树的正确姿势是维护一个cover记录压的层数,删除线段在对应的cover删除层数,然后记录一下这个区间总共的线段长度

最后维护到根就是我们想要的当前线段并

然后又有很多奇奇怪怪的问题

……例如数组越界导致的WA

 /*
ID: ivorysi
LANG: C++
PROG: picture
*/
#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define sigongzi(j,x,y) for(int j=(x);j>(y);--j)
#define inf 0x5f5f5f5f
#define ivorysi
#define mo 97797977
#define hash 974711
#define base 47
#define fi first
#define se second
#define pii pair<int,int>
#define esp 1e-8
typedef long long ll;
using namespace std;
struct node {
int s,t,y,on;
node (int st=, int ed=, int ceng=, int num=) {//@要写上初值
s=st;t=ed;y=ceng;on=num;
}
bool operator <(const node &b) const {
return y<b.y || (y==b.y && on>b.on);
}
}seg[];
struct data {
int l,r,len,cover;
}tree[];
int n;
int lx[],rx[],ly[],ry[],ans;
void maketree(int u,int l,int r) {
tree[u].l=l;tree[u].r=r;
tree[u].len=tree[u].cover=;
if(l==r) {
return;
}
int mid=(l+r)>>;
maketree(u<<,l,mid);
maketree(u<<|,mid+,r);
}
void check(int u) {
if(tree[u].cover>) tree[u].len=tree[u].r-tree[u].l+;
else if(tree[u].l==tree[u].r) tree[u].len=;
//@少了这句话数组会越界从而导致……WA?开到200000可以解决问题
else tree[u].len=tree[u<<].len+tree[u<<|].len;
}
void change(int u,int x,int y,int c) {
if(x<=tree[u].l && tree[u].r<=y) {
tree[u].cover+=c;
check(u);
//@如果维护lazy:
//@像区间加一样操作,可能覆盖到的地方没有办法及时减掉,因为矩形的边都是成对出现
return;
}
int mid=(tree[u].l+tree[u].r)>>;
if(x<=mid) {change(u<<,x,y,c);}
if(y>mid) {change(u<<|,x,y,c);}
check(u);
}
void solve() {
scanf("%d",&n);
siji(i,,n) {
scanf("%d%d%d%d",&lx[i],&ly[i],&rx[i],&ry[i]);
}
siji(i,,n) {
seg[i*-] = node(lx[i],rx[i],ly[i],);
seg[i*] = node(lx[i],rx[i],ry[i],-);
}
sort(seg+,seg+*n+);
maketree(,-,);//@其实如果是浮点数就需要hash了,然而并不需要
siji(i,,*n) {
int temp=tree[].len;
change(,seg[i].s,seg[i].t-,seg[i].on);
ans+=abs(temp-tree[].len);
}
siji(i,,n) {
seg[i*-] = node(ly[i],ry[i],lx[i],);
seg[i*] = node(ly[i],ry[i],rx[i],-);
}
sort(seg+,seg+*n+);
maketree(,-,);
siji(i,,*n) {
int temp=tree[].len;
change(,seg[i].s,seg[i].t-,seg[i].on);
ans+=abs(temp-tree[].len);
}
printf("%d\n",ans);
}
int main(int argc, char const *argv[])
{
#ifdef ivorysi
freopen("picture.in","r",stdin);
freopen("picture.out","w",stdout);
#else
freopen("f1.in","r",stdin);
#endif
solve();
return ;
}
 

最新文章

  1. firefox,跨域ajax 调用方法
  2. [Android基础论]为何Activity退出之后,系统没有调用onDestroy方法?
  3. Android 初始化Setup Wizard——Provision
  4. [ruby on rails] 跟我学之(8)修改数据
  5. SQL基础--&amp;gt; 约束(CONSTRAINT)
  6. LUA 捕获模式 URL编码的例子解析
  7. jQuery 快速结束当前动画
  8. 【转】java JTable排序和过滤
  9. springcloud(四):熔断器Hystrix
  10. 分布式memcached-虚拟节点
  11. Access第一周总结
  12. AWS的开发工具包和设备SDK开发工具包
  13. Centos 7 安装composer和Laravel
  14. [学习笔记]Javascript采用二进制浮点数和四舍五入的错误
  15. (转) 为什么选择.NETCore?
  16. LInux 文件系统 tmpfs 分区不显示解决
  17. python标准库介绍——22 UserList 模块详解
  18. office 2010 安装时出错 MSXML版本6.10.1129.0
  19. Dubbo 自定义异常,你是怎么处理的?
  20. angularJS控制器之间的相互通信方式、$broadcast、$emit、$on

热门文章

  1. Java基础-IO流对象之内存操作流(ByteArrayOutputStream与ByteArrayInputStream)
  2. js 正则表达式 整数或小数
  3. 3.Filter和interceptor的区别
  4. 蓝桥杯 算法提高 学霸的迷宫 经典BFS问题
  5. TV 开发相关
  6. 6 Easy Steps to Learn Naive Bayes Algorithm (with code in Python)
  7. 编辑器之王:Emacs 和 Vim
  8. 选择Memcached还是Redis?
  9. Anaconda+django写出第一个web app(三)
  10. lucene总结——(十七)