Section 3.1 Shaping Regions

卡这题卡了一天左右,不会做额,找了不少的代码和解说,发现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]);
    }
}

你可能感兴趣的:(ping)