CF - 872E. Points, Lines and Ready-made Titles - 并查集+数学

探讨在平面直角坐标系上,通过给定点绘制横线或竖线的不同组合方式,利用并查集解决点的合并问题,并计算最终组合数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

E. Points, Lines and Ready-made Titles

分类: disjoint set math

1.题意概述
  • 平面直角坐标系上有 n(1n105) 个点,你可以在每个点画一条横线或一条竖线或者不划线。问你构成的图像有多少种不同的图?答案取模 109+7
2.解题思路
  • 考虑这个画图的特殊性,问题可以转化为:这些点可以产生的横线与竖线的出现情况。

    若单独考虑每个点情况显然是 3n 种,但是现在情况就是可能点在同一行或一列,对于这种情况,我们考虑缩点后分情况考虑:对于二维坐标系中的每一行中,对于每个点,如果右边有点,则从该点向右边这个点(相邻的那个点)连一条单向边。对于每一列中,对于每个点,如果该点下面有点,则向下边这个点(相邻的那个点)连一条单向边。然后对于每个点,将与之相连的点并查集合并,合并时如果发现成环,则记这个缩点的集合tag[i]=1,否则tag[i]=0。再算出每个集合中所有点所占据的不重复的行列数,记为dif[i]

  • 单独考虑缩点后的集合内情况,如图:aaa

3.AC代码
const int maxn = 1e5 + 10;
ll quickmod(ll a, ll b) {
    ll ans = 1;
    while (b) {
        if (b & 1) ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod; 
    }
    return ans;
}
struct Point {
    int x, y, id;
} p[maxn];
inline bool cmp1(const Point& a, const Point& b) {
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}
inline bool cmp2(const Point& a, const Point& b) {
    return a.y < b.y || (a.y == b.y && a.x < b.x);
}
inline bool cmp3(const Point& a, const Point& b) {
    return a.id < b.id;
}
int pa[maxn], dif[maxn];
bool tag[maxn];
int find(int x) {
    if (x == pa[x]) return x;
    return pa[x] = find(pa[x]);
}
struct Edge {
    int to, next;
} E[maxn << 2];
int head[maxn], cnt;
inline void addedge(int u, int v) {
    E[cnt].to = v;
    E[cnt].next = head[u];
    head[u] = cnt++;
}
set<int> x[maxn], y[maxn];
inline void solve() {
    int n;
    scanf("%d", &n);
    cnt = 0;
    memset(head, -1,sizeof head);
    memset(tag, 0, sizeof tag);
    memset(dif, 0, sizeof dif);
    rep(i, 1, n + 1) {
        pa[i] = p[i].id = i;
        scanf("%d%d", &p[i].x, &p[i].y);
    }
    sort(p + 1, p + n + 1, cmp1);
    rep(i, 1, n) {
        if (p[i].x == p[i + 1].x) {
            addedge(p[i].id, p[i + 1].id);
        }
    }
    sort(p + 1, p + n + 1, cmp2);
    rep(i, 1, n) {
        if (p[i].y == p[i + 1].y) {
            addedge(p[i].id, p[i + 1].id);
        }
    }
    sort(p + 1, p + n + 1, cmp3);
    rep(u, 1, n + 1) {
        int fu = find(u);
        for (int i = head[u]; ~i; i = E[i].next) {
            int v = E[i].to;
            int fv = find(v);
            if (fu == fv) tag[fu] = 1;
            else {
                pa[fv] = fu;
                tag[fu] |= tag[fv];
            }
        }
    }
    rep(u, 1, n + 1) {
        int fa = find(u);
        int sx = p[u].x, sy = p[u].y;
        if (x[fa].find(sx) == x[fa].end()) {
            dif[fa]++;
            x[fa].insert(sx);
        }
        if (y[fa].find(sy) == y[fa].end()) {
            dif[fa]++;
            y[fa].insert(sy);
        }
    } 
    ll ans = 1;
    rep(u, 1, n + 1) {
        if (u == find(u)) {
            ll res = quickmod(2LL, 1LL * dif[u]);
            if (!tag[u]) --res;
            ans = ans * res % mod;
        }
    }
    printf("%lld\n", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值