Codeforces div2 E. Count The Rectangles (垂直平行线段围成的矩形计数)

https://codeforces.com/contest/1194/problem/E

这道题是ZOJ1426的超级强化版,数据大了很多。给了两种解法,前一种思维简单,好理解。

题意:给了n条线段,线段不重复,而且不是竖线就是横线,问这n条线段能围城多少个矩形。

做法:首先,由于n有5000,对半开枚举横线和竖线是不行的。

我们这样考虑,我们先把竖线和横线分别存储起来。然后可以往暴力一点方向想,最后在进行优化。

稍微暴力一点的就是先判断一下,每一条竖线与多少条竖线相交,然后在判断任意两条竖线,公共的相交横线有几条,然后取一个组合数,就是对答案的一部分贡献。

但是怎么记录呢?事实证明数组标记然后暴力判断是会T的。

但不过有一个bitset的东西,我们开几个bitset,如果第i条竖线与第j条横线相交,那么第i个bitset的第j位置为1;

然后判断第i和第j条竖线公共的相交横线有几条,只需要将i和j相&然后count就行了,事实证明这样的复杂度可以接受的。

就下来的复杂的就与竖线的条数有关,最大有5000这个多半会T,事实也T了,既然竖线多了,那么横线就少了,所以可以将竖线和横线对换,这样这道题就可以过了。

#include<bits/stdc++.h>
 
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 200000 + 10;
const int inf = 0x3f3f3f3f;
struct node {
    int x1, y1, x2, y2;
} line[N];
vector <node> v1, v2;
int n;
 
int belong(node a, node b) {
    if (a.x1 >= b.x1 && a.x1 <= b.x2 && b.y1 >= a.y1 && b.y1 <= a.y2)
        return 1;
    if (b.x1 >= a.x1 && b.x1 <= a.x2 && a.y1 >= b.y1 && a.y1 <= b.y2)
        return 1;
    return 0;
}
 
bitset<5010> mp[5010];
 
int main() {
 
    scanf("%d", &n);
    int a, b, c, d;
    for (int i = 1; i <= n; i++) {
        scanf("%d%d%d%d", &a, &b, &c, &d);
        if (a == c) {
            if (b > d)swap(b, d);
            v1.push_back(node{a, b, c, d});
        }
        if (b == d) {
            if (a > c)swap(a, c);
            v2.push_back(node{a, b, c, d});
        }
    }
    ll ans = 0;
    if (v1.size() > v2.size()) swap(v1, v2);
    for (int i = 0; i < v1.size(); i++) {
        for (int j = 0; j < v2.size(); j++) {
            if (belong(v1[i], v2[j]))
                mp[i][j] = 1;
        }
        for (int j = 0; j < i; j++) {
            ll ret = (mp[i] & mp[j]).count();
            ans += ret * (ret - 1) / 2;
        }
    }
    cout << ans << endl;
    return 0;
}

但不过上面这个复杂度也不怎么行,还容易被卡。因此可以用一种数据结构对公共相交线进行优化。

首先对竖线按照x进行排序,然后对第i条竖线相交的横线全部记录下来。

然后对其按照左端点进行排序,也可以右端点。

对于第j条竖线,进行遍历由于有序,把左端点小于j的x的横线记录下来。

这些横线在j条竖线的y1,y2的范围相交。然后用树状数组求和就完了。

如果不清楚画一个图就清楚了,具体这道题如何也只有本人自己做了才清楚。

#include "bits/stdc++.h"

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 2e5 + 10;
const int M = 5002;

struct line {
    int p, l, r;

    bool operator<(line a) const { return p < a.p; }
} p[N], q[N];

struct node {
    int p, r;

    bool operator<(node a) const { return r < a.r; }
} l[N];

int n, cnt1, cnt2, low[M * 2];

void update(int x, int val) { for (; x < M * 2; x += x & -x) low[x] += val; }

int query(int x) {
    int ret = 0;
    for (; x; x -= x & -x) ret += low[x];
    return ret;
}

int belong(line v, line h) {
    if (v.p >= h.l && v.p <= h.r && h.p >= v.l && h.p <= v.r)
        return 1;
    return 0;
}

int main() {
    scanf("%d", &n);
    int a, b, c, d;
    for (int i = 1; i <= n; i++) {
        scanf("%d%d%d%d", &a, &b, &c, &d);
        a += M, b += M, c += M, d += M;
        if (a == c) {
            if (b > d) swap(b, d);
            p[++cnt1] = line{a, b, d};
        }
        if (b == d) {
            if (a > c) swap(a, c);
            q[++cnt2] = line{b, a, c};
        }
    }
    ll ans = 0;
    sort(p + 1, p + cnt1 + 1);
    for (int i = 1; i <= cnt1; i++) {
        int cnt = 0;
        for (int j = 1; j <= cnt2; j++) {
            if (belong(p[i], q[j])) {
                l[++cnt] = node{q[j].p, q[j].l};
            }
        }
        sort(l + 1, l + cnt + 1);
        memset(low, 0, sizeof(low));
        for (int j = 1, k = 1; j < i; j++) {
            for (; k <= cnt && l[k].r <= p[j].p; k++) update(l[k].p, 1);
            ll ret = query(p[j].r) - query(p[j].l - 1);
            ans += ret * (ret - 1) / 2;
        }
    }
    printf("%lld\n", ans);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值