• 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]);
        }
    }
  • 相关阅读:
    编译安装LEMP
    eAccelerator配置和使用指南
    /dev/null的用途
    分库分表
    JVM
    SOFA 数据透析
    HTTPS
    SOFA 通信
    分布式锁
    mysql 锁
  • 原文地址:https://www.cnblogs.com/litstrong/p/1971189.html
Copyright © 2020-2023  润新知