codeforces 871C. Points, Lines and Ready-made Titles (与图相关的计数)

题目链接

C. Points, Lines and Ready-made Titles

分析

 可以发现如果将相邻两个点(X,或者Y坐标相同)用一条边连起来,建一个无向图,那麽就可以单独考虑连通分量了,而一个连通分量中可以发现如果没有环,那麽答案是
 

2X+Y1 X:=X Y:=

 如果有环
  2X+Y
  官方证明
 其实只需要简单判定X+Y与连通分量点数的关系就可以知道是否有环

AC code

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define fi first
#define se second
#define mp make_pair
#define pb push_back
typedef long long LL;
const int maxn = 2e5+10;
const int MOD = 1e9+7;

LL num[maxn];
std::vector<int> G[maxn];
struct Point{
    int val[2];
    int id;
    bool operator<(const Point &x)const{
        return id < x.id;
    }
};
Point a[maxn];
int CMP_IDX = 0;
bool cmp(const Point x,const Point y){
    if(x.val[CMP_IDX] == y.val[CMP_IDX])return x.val[CMP_IDX^1]<y.val[CMP_IDX^1];
    else return x.val[CMP_IDX]<y.val[CMP_IDX];
}
inline void add_edge(int x,int y){
    G[x].pb(y);G[y].pb(x);
}

bool vis[maxn];
unordered_set<int> X,Y;
int cnt =0;
LL ans = 1;
void dfs(int u) {
    vis[u] = 1;
    X.insert(a[u].val[0]);
    Y.insert(a[u].val[1]);
    cnt++;
    for(auto e : G[u]){
        if(!vis[e]){
            dfs(e);
        }
    }
}

int main()
{
    num[0] =1;
    for(int i=1 ; i<maxn ; ++i){
        num[i] = 2*num[i-1] % MOD;
    }
    int n;
    cin>>n;
    for(int i=1 ; i<=n ; ++i){
        scanf("%d%d",&a[i].val[0],&a[i].val[1] );
        a[i].id = i;
    }
    for(CMP_IDX =0 ; CMP_IDX<=1 ; ++CMP_IDX){
        sort(a+1,a+1+n,cmp);
        for(int i=1 ; i<n ; ++i)
            if(a[i].val[CMP_IDX] == a[i+1].val[CMP_IDX])
                add_edge(a[i].id,a[i+1].id);
    }
    sort(a+1,a+n+1);
    memset(vis,0,sizeof(vis));
    for(int i=1 ; i<=n ; ++i){
        if(!vis[i]){
            X.clear();
            Y.clear();
            cnt=0;
            dfs(i);
            int sz= X.size()+Y.size();
            if(cnt+1==sz){
                ans = ans*(num[sz]-1+MOD)%MOD;//无环
            }else ans = ans*num[sz]%MOD;
        }
    }
    std::cout << ans << '\n';
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值