• Codeforces 870E Points, Lines and Ready-made Titles 计数


    题目链接

    题意

    给定二维坐标上的(n)个点,过每个点可以 画一条水平线 或 画一条竖直线 或 什么都不画,并且若干条重合的直线被看做同一条。问共可能得到多少幅不同的画面?

    题解

    官方题解

    仆の瞎扯

    bzoj 1854的并查集思路蜜汁契合
    // 看完了题解的我这样想道

    首先显然可以将图分为若干个联通块。

    且慢,哪里来的图?
    那就先考虑建图?
    不急不急,先来想想看每一个联通块的性质。

    如果该联通块中有环的话,肯定每条边都能取到;如果联通块是一棵树,那么必有一条边取不到(具体阐述见上bzoj 1854),所以只需要知道

    1. 这个联通块中有多少条边
    2. 这个联通块是不是环

    这两个信息就可以了

    那么可以直接上并查集。

    什么样的点可以并到一起呢?横坐标相同的或者纵坐标相同的。

    有没有环怎么维护呢?看有没有加进去的边的端点本身就在一个集合里。

    联通块中边的数目又怎么知道呢?这倒还挺有意思的,其实只要直接看出现过多少个横坐标或者纵坐标就可以了,因为一个横坐标或者一个纵坐标就代表一条可以选的直线,所以这块联通块的贡献就是(2^{x+y}或者2^{x+y}-1)

    然后呢?就做完了。

    然而呢?比赛结束。一天了。

    然后再推荐一下葫芦爷的题解太强辣

    Code

    #include <bits/stdc++.h>
    #define maxn 100010
    using namespace std;
    typedef long long LL;
    const LL mod = 1e9+7;
    struct node {
        int x, y;
    }a[maxn];
    int fa[maxn], sz[maxn], f[maxn], id[maxn], m[maxn];
    bool circ[maxn], vis[maxn];
    vector<int> v[maxn];
    set<int> sx, sy;
    bool cmp1(int i, int j) {
        return a[i].x < a[j].x || (a[i].x == a[j].x && a[i].y < a[j].y);
    }
    bool cmp2(int i, int j) {
        return a[i].y < a[j].y || (a[i].y == a[j].y && a[i].x < a[j].x);
    }
    LL poww(LL a, LL b) {
        LL ret = 1;
        while (b) {
            if (b & 1) (ret *= a) %= mod;
            (a *= a) %= mod;
            b >>= 1;
        }
        return ret;
    }
    int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
    void unionn(int a, int b) {
        a = find(a), b = find(b);
        if (a == b) { circ[a] = true; return; }
        if (sz[a] > sz[b]) swap(a, b);
        fa[a] = b; sz[b] += sz[a];
        circ[b] |= circ[a];
    }
    int main() {
        int n;
        scanf("%d", &n);
        for (int i = 0; i < n; ++i) {
            scanf("%d%d", &a[i].x, &a[i].y);
        }
        for (int i = 0; i < n; ++i) id[i] = i;
        for (int i = 0; i < n; ++i) fa[i] = i, sz[i] = 1;
    
        sort(id, id+n, cmp1);
        for (int i = 1; i < n; ++i) {
            if (a[id[i]].x == a[id[i-1]].x) unionn(id[i-1], id[i]);
        }
        sort(id, id+n, cmp2);
        for (int i = 1; i < n; ++i) {
            if (a[id[i]].y == a[id[i-1]].y) unionn(id[i-1], id[i]);
        }
        for (int i = 0; i < n; ++i) fa[i] = find(i);
    
        int tot = -1;
        for (int i = 0; i < n; ++i) {
            if (!vis[fa[i]]) vis[fa[i]] = true, f[++tot] = fa[i], m[fa[i]] = tot;
            v[m[fa[i]]].push_back(i);
        }
        LL ans = 1;
        for (int i = 0; i <= tot; ++i) {
            sx.clear(), sy.clear();
            for (auto idx : v[i]) {
                sx.insert(a[idx].x), sy.insert(a[idx].y);
            }
            LL mul = poww(2, sx.size()+sy.size());
            if (!circ[f[i]]) (mul += mod-1) %= mod;
            (ans *= mul) %= mod;
        }
        printf("%I64d
    ", ans);
        return 0;
    }
    
    
  • 相关阅读:
    表空间的改变
    特殊字符转换
    oracle下创建临时表
    约束
    删除数据库记录的同时删除磁盘文件
    数据文件
    级联删除case
    oracle net连接方式

    在Oracle服务器端配置监听器
  • 原文地址:https://www.cnblogs.com/kkkkahlua/p/7679526.html
Copyright © 2020-2023  润新知