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\n", (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('\n');
    for(int i = 1; i <= 2200; ++i)
        putchar('4');
    putchar('5');
    putchar('\n');
}

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\n", 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\n", 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\n", 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\n", xx[x1[i]], yy[y1[i]]);
            return;
        }
        if(check(x1[i], y2[i])) {
            printf("%d %d\n", xx[x1[i]], yy[y2[i]]);
            return;
        }
        if(check(x2[i], y1[i])) {
            printf("%d %d\n", xx[x2[i]], yy[y1[i]]);
            return;
        }
        if(check(x2[i], y2[i])) {
            printf("%d %d\n", 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\n", 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\n", xx[X], yy[Y]);
            return;
        }
    }
    printf("-1 -1\n");
    exit(-1);
}

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

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

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

posted @ 2020-01-24 01:46  KisekiPurin2019  阅读(115)  评论(0编辑  收藏  举报