zoukankan      html  css  js  c++  java
  • AIM Tech Round 5 (rated, Div. 1 + Div. 2)

    A - Find Square

    题意:给一个n*m的矩阵全是字符'W',其中一块奇数边长的正方形变成了'B',找出其中心。

    题解:逐个扫描可以找到右下角,然后首次找到的是左上角。取平均。

    没意思。

    int n, m;
    char g[205][205];
    
    void test_case() {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i)
            scanf("%s", g[i] + 1);
        int l1 = -1, l2 = -1;
        int s1 = -1, s2 = -1;
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= m; ++j) {
                if(g[i][j] == 'B') {
                    l1 = i;
                    l2 = j;
                    if(s1 == -1)
                        s1 = i;
                    if(s2 == -1)
                        s2 = j;
                }
            }
        }
        printf("%d %d
    ", (s1 + l1) / 2, (s2 + l2) / 2);
    }
    

    B - Unnatural Conditions

    题意:记s(x)为x的十进制各位之和,给定n和m(>=1),要求构造两个a,b,满足:

    s(a)>=n

    s(b)>=n

    s(a+b)<=m

    a,b长度不能超过2230位。

    构造一种进位之后恰好最高位是1的做法就可以了。然后尽可能长。

    int n, m;
    
    void test_case() {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= 2200; ++i)
            putchar('5');
        putchar('5');
        putchar('
    ');
        for(int i = 1; i <= 2200; ++i)
            putchar('4');
        putchar('5');
        putchar('
    ');
    }
    

    C - Rectangles

    题意:给n个1e9范围内的矩形,求一个点被至少n-1个矩形覆盖。多解输出其中任意一个。

    题解:无解输出什么?一种简单的想法是扫描线。从下到上扫描,记录每个矩形的入口和出口,在线段树上面区间+1,当某个点被覆盖超过n-1次时输出这个点。想了另一种假做法:与某点不相交的矩形,要么完全在左边,要么完全在右边。另一方面要么完全在上边,要么完全在下边。这种解法把x和y分开看导致错误。

    假算法:

    int n;
    int x1[150005], y1[150005];
    int x2[150005], y2[150005];
    int xx[300005], yy[300005], top, xtop, ytop;
    
    int dx[300005], dy[300005];
    int dx2[300005], dy2[300005];
    
    bool check(int x, int y) {
        printf("x=%d y=%d
    ", x, y);
        int LX = dx2[x - 1];
        int RX = n - dx[x];
        if(LX + RX >= 2)
            return false;
        int LY = dy2[y - 1];
        int RY = n - dy[y];
        if(LY + RY >= 2)
            return false;
        printf("LX=%d RX=%d LY=%d RY=%d
    ", LX, RX, LY, RY);
        return true;
    }
    
    void test_case() {
        scanf("%d", &n);
        top = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d%d%d%d", &x1[i], &y1[i], &x2[i], &y2[i]);
            ++top;
            xx[top] = x1[i];
            yy[top] = y1[i];
            ++top;
            xx[top] = x2[i];
            yy[top] = y2[i];
        }
        sort(xx + 1, xx + 1 + top);
        xtop = unique(xx + 1, xx + 1 + top) - (xx + 1);
        sort(yy + 1, yy + 1 + top);
        ytop = unique(yy + 1, yy + 1 + top) - (yy + 1);
        for(int i = 1; i <= n; ++i) {
            x1[i] = lower_bound(xx + 1, xx + 1 + xtop, x1[i]) - (xx);
            x2[i] = lower_bound(xx + 1, xx + 1 + xtop, x2[i]) - (xx);
            y1[i] = lower_bound(yy + 1, yy + 1 + ytop, y1[i]) - (yy);
            y2[i] = lower_bound(yy + 1, yy + 1 + ytop, y2[i]) - (yy);
            printf("%d %d %d %d
    ", x1[i], y1[i], x2[i], y2[i]);
        }
        for(int i = 1; i <= n; ++i) {
            ++dx[x1[i]];
            ++dx2[x2[i]];
            ++dy[y1[i]];
            ++dy2[y2[i]];
        }
        for(int i = 1; i <= xtop; ++i) {
            dx[i] += dx[i - 1];
            dx2[i] += dx2[i - 1];
        }
        for(int i = 1; i <= ytop; ++i) {
            dy[i] += dy[i - 1];
            dy2[i] += dy2[i - 1];
        }
        for(int i = 1; i <= n; ++i) {
            if(check(x1[i], y1[i])) {
                printf("%d %d
    ", xx[x1[i]], yy[y1[i]]);
                return;
            }
            if(check(x1[i], y2[i])) {
                printf("%d %d
    ", xx[x1[i]], yy[y2[i]]);
                return;
            }
            if(check(x2[i], y1[i])) {
                printf("%d %d
    ", xx[x2[i]], yy[y1[i]]);
                return;
            }
            if(check(x2[i], y2[i])) {
                printf("%d %d
    ", xx[x2[i]], yy[y2[i]]);
                return;
            }
        }
    }
    

    x和y是不能分开考虑的,过不了第一个样例。

    正确做法:扫描线。注意这里有一些需要思考的地方,这里的出口只是应该在y的坐标输入进来的时候+1,而不能在离散化之后+1,x是不需要出口的,因为闭区间写法的线段树不是差分。找到最大值之后可以顺便返回最大值对应的x的位置。

    int n;
    int x1[150005], y1[150005];
    int x2[150005], y2[150005];
    int xx[300005], yy[300005], top, xtop, ytop;
    
    vector<pii> upd[300005];
    
    struct SegmentTree {
    #define ls (o<<1)
    #define rs (o<<1|1)
        static const int MAXN = 300000;
        static const int INF = 0x3f3f3f3f;
        int ma[(MAXN << 2) + 5];
        int ps[(MAXN << 2) + 5];
        int lz[(MAXN << 2) + 5];
    
        void PushUp(int o) {
            if(ma[ls] >= ma[rs]) {
                ma[o] = ma[ls];
                ps[o] = ps[ls];
            } else {
                ma[o] = ma[rs];
                ps[o] = ps[rs];
            }
        }
    
        void PushDown(int o, int l, int r) {
            if(lz[o]) {
                lz[ls] += lz[o];
                lz[rs] += lz[o];
                ma[ls] += lz[o];
                ma[rs] += lz[o];
                lz[o] = 0;
            }
        }
    
        void Build(int o, int l, int r) {
            if(l == r) {
                ma[o] = 0;
                ps[o] = l;
            } else {
                int m = l + r >> 1;
                Build(ls, l, m);
                Build(rs, m + 1, r);
                PushUp(o);
            }
            lz[o] = 0;
        }
    
        void Update(int o, int l, int r, int ql, int qr, int v) {
            if(ql <= l && r <= qr) {
                lz[o] += v;
                ma[o] += v;
            } else {
                PushDown(o, l, r);
                int m = l + r >> 1;
                if(ql <= m)
                    Update(ls, l, m, ql, qr, v);
                if(qr >= m + 1)
                    Update(rs, m + 1, r, ql, qr, v);
                PushUp(o);
            }
        }
    
        pii QueryMax(int o, int l, int r, int ql, int qr) {
            if(ql <= l && r <= qr) {
                return {ma[o], ps[o]};
            } else {
                PushDown(o, l, r);
                int m = l + r >> 1;
                pii res = {-INF, -1};
                if(ql <= m)
                    res = QueryMax(ls, l, m, ql, qr);
                if(qr >= m + 1) {
                    pii tmp = QueryMax(rs, m + 1, r, ql, qr);
                    if(tmp.first > res.first)
                        res = tmp;
                }
                return res;
            }
        }
    #undef ls
    #undef rs
    } st;
    
    void test_case() {
        scanf("%d", &n);
        top = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d%d%d%d", &x1[i], &y1[i], &x2[i], &y2[i]);
            ++y2[i];
            ++top;
            xx[top] = x1[i];
            yy[top] = y1[i];
            ++top;
            xx[top] = x2[i];
            yy[top] = y2[i];
        }
        sort(xx + 1, xx + 1 + top);
        xtop = unique(xx + 1, xx + 1 + top) - (xx + 1);
        sort(yy + 1, yy + 1 + top);
        ytop = unique(yy + 1, yy + 1 + top) - (yy + 1);
        for(int i = 1; i <= n; ++i) {
            x1[i] = lower_bound(xx + 1, xx + 1 + xtop, x1[i]) - (xx);
            x2[i] = lower_bound(xx + 1, xx + 1 + xtop, x2[i]) - (xx);
            y1[i] = lower_bound(yy + 1, yy + 1 + ytop, y1[i]) - (yy);
            y2[i] = lower_bound(yy + 1, yy + 1 + ytop, y2[i]) - (yy);
            //printf("%d %d %d %d
    ", x1[i], y1[i], x2[i], y2[i]);
            upd[y1[i]].push_back({x1[i], x2[i]});
            upd[y2[i]].push_back({-x1[i], -x2[i]});
        }
        st.Build(1, 1, xtop);
        for(int i = 1; i <= ytop; ++i) {
            for(int j = 0; j < upd[i].size(); ++j) {
                int l = upd[i][j].first;
                int r = upd[i][j].second;
                if(l > 0 && r > 0)
                    st.Update(1, 1, xtop, l, r, 1);
                else
                    st.Update(1, 1, xtop, -l, -r, -1);
            }
            pii res = st.QueryMax(1, 1, xtop, 1, xtop);
            if(res.first >= n - 1) {
                int X = res.second;
                int Y = i;
                printf("%d %d
    ", xx[X], yy[Y]);
                return;
            }
        }
        printf("-1 -1
    ");
        exit(-1);
    }
    

    更快的做法:注意到n-1个矩形的交也是一个矩形(可能为空)。取矩形的前缀交,和后缀交,那么假如把第i个矩形排除,则pre[i-1]和suf[i+1]的交中的任何一点都是解。这个是这个问题是n-1的一种特殊解法。假如n-2就没辙了。还是扫描线比较高级又通用。

    不过我当时比赛的时候是怎么会知道题解这种做法的呢?可能因为我当时不会扫描线吧。几乎全部人都是用题解的解法的,包括当时的我。

    那么求矩形的交很简单,取最右的左边界,最上的下边界,最左的右边界,最下的上边界就可以了。

  • 相关阅读:
    Java 二维数组
    JAVA——运算符
    JAVA——类与对象
    Eclipse无法自动编译出class文件 转
    引导入单用户模式
    安装sql server遇到问题解决方法
    数据结构和算法分析学习
    开源软件
    POP3、SMTP、IMAP和Exchange都是个什么玩意?
    邮件相关名词 MUA, MTA
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12231779.html
Copyright © 2011-2022 走看看