卡这题卡了一天左右,不会做额,找了不少的代码和解说,发现NOCOW这里很不错,有大牛在这里写方法,可以拓宽思路,这几个1,2,3还不错,看懂了,和HDU1543是同一道题,就说说题意和方法吧。
给定一个A*B的矩形,A<10^4,B<10^4,然后可以往这个矩形上涂矩形状的颜色,矩形和X,Y轴平行,如果涂到同一个地方,则覆盖原来的颜色,颜色C<2500,有N<1000个矩形,求最后剩下的各个颜色的个数。
有很多方法了,矩形分割,线段树,并查集优化,等等,但调了线段树来看,顺便复习下线段树。用二维实现的话,空间很紧,于是就牺牲时间换空间,用一维的线段树来实现,离散化然后扫描线。我将矩形的x方向的值离散化掉,然后扫描从左到右,对于每两个线x1,x2,先创建一颗线段树,按读入顺序遍历矩形,判断是否覆盖x1,x2,如果覆盖,则该矩形对该区间覆盖有效,则将该Y方向上的线段插入线段树中,之后,统计树上的颜色值。
想到的有两个细节:
1. 遍历矩形的顺序可以按实现的方法来选择,反过来的话,即涂过的地方就不涂了.
2. 原先的矩形都是颜色1,可以事后再统计.
这样子就可以通过USACO上的题了,注意在线段树上涂色的方法实现,详情见代码中加*处,线段树的原理之一就是基于子树就是当前数的子集划分,由它代替所有子树,如果没必要时,在当前树这儿就可以停止了,但如果继续深入的话,要注意子树的数据是否是更新的,如果不是更新的,要注意更新。下面的代码在第11组数据处花了1.7+s时间,可以把Y方向和颜色C都离散化掉,应该会快的,就没改了。
接下来把矩形分割的方法学一下,线段树+离散化+扫描线的代码如下:
/*
ID: litstrong
PROG: rect1
LANG: C++
*/
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <math.h>
#include <queue>
#include <algorithm>
#include <string.h>
#include <stdio.h>
using namespace std;
const int MAXN = 10005;
const int MAXC = 2505;
int c_cnt[MAXC];
int c_ans[MAXC];
int x_line[MAXN * 2];
class CSEG
{
public:
int L, R, C;
//C表示颜色
public:
CSEG() {}
CSEG(int _L, int _R, int _C) : L(_L), R(_R), C(_C) {}
};
//区间树,线段树的变形
class CSEGTREE
{
public:
CSEG segs[MAXN * 5];
public:
void Build(int root, int L, int R)
{
segs[root] = CSEG(L, R, 1);
int M = (L + R) / 2;
//printf("%d %d %d\n", root, L, R);
if(R - L > 1) //非叶子的话扩展
{
if(L < M) Build(2 * root, L, M);
if(M < R) Build(2 * root + 1, M, R);
}
}
void Insert(int root, int line_L, int line_R, int C)
{
//segs[root].L <= segs[root].R
//printf("%d %d %d %d %d\n", root, segs[root].L, segs[root].R, line_L, line_R);
if(line_L <= segs[root].L && line_R >= segs[root].R) //完全覆盖
{
segs[root].C = C; //下面的不更新了,之后再更新
return;
}
if(segs[root].C != -1)
{
if(segs[root].C == C) return; //剪枝
//否则扩展,记住这时往下更新
segs[2 * root].C = segs[2 * root + 1].C = segs[root].C; //******
}
segs[root].C = -1; //部分覆盖,标记为混色,用于查询时的优化
int M = (segs[root].L + segs[root].R) / 2;
if(line_R > M) Insert(2 * root + 1, line_L, line_R, C);
if(line_L < M) Insert(2 * root, line_L, line_R, C);
}
void Query(int root)
{
if(segs[root].C != -1)
{
c_cnt[segs[root].C] += (segs[root].R - segs[root].L);
return;
}
Query(2 * root);
Query(2 * root + 1);
}
};
class CRECT
{
public:
int nx, mx, ny, my, c;
public:
CRECT() {}
CRECT(int _nx, int _mx, int _ny, int _my, int _c) :
nx(_nx), mx(_mx), ny(_ny), my(_my), c(_c) {}
}rect[MAXN];
int main()
{
freopen("rect1.in", "r", stdin);
freopen("rect1.out", "w", stdout);
int A, B, N;
scanf("%d%d%d", &A, &B, &N);
for(int i = 0; i < N; i++)
{
int nx, mx, ny, my, c;
scanf("%d%d%d%d%d", &nx, &ny, &mx, &my, &c);
rect[i] = CRECT(min(nx, mx), max(nx, mx), min(ny, my), max(ny, my), c);
x_line[2 * i] = nx;
x_line[2 * i + 1] = mx;
}
sort(x_line, x_line + 2 * N);
memset(c_ans, 0, sizeof(c_ans));
CSEGTREE tree;
for(int i = 1; i < 2 * N; i++)
{
if(x_line[i - 1] == x_line[i]) continue;
tree.Build(1, 0, B);
//printf("Build ok\n");
memset(c_cnt, 0, sizeof(c_cnt));
for(int j = 0; j < N; j++)
{
if(rect[j].nx <= x_line[i - 1] &&
rect[j].mx >= x_line[i])
{
tree.Insert(1, rect[j].ny, rect[j].my, rect[j].c);
//printf("Insert ok\n");
}
}
tree.Query(1);
for(int j = 0; j < MAXC; j++)
{
c_ans[j] += c_cnt[j] * (x_line[i] - x_line[i - 1]);
}
//printf("#%d, %d, %d*%d=%d\n", c_cnt[1], c_cnt[3], c_cnt[2], (x_line[i] - x_line[i - 1]), c_cnt[2] * (x_line[i] - x_line[i - 1]));
}
c_ans[1] += (A - x_line[2 * N - 1] + x_line[0]) * B;
for(int i = 0; i < MAXC; i++)
{
if(c_ans[i]) printf("%d %d\n", i, c_ans[i]);
}
}