Codeforces Round #440 Div. 2 E - Points, Lines and Ready-made Titles

纪念第一次把CF补(chao)完。

题目链接

推荐一篇大佬博客,不过有部分笔误,下面已做修改。

转自:https://blog.csdn.net/calabash_boy/article/details/78249446

题意:给出N个二维点,坐标都是整数,每个点可以不画线,也可以画一条竖直的线,也可以画一条水平的线,但是最多只能画一条线。 求图案方案数。

题解: 假设两个点他们相互独立,那么答案就是组合一下,可以分别来算。那么思路就是:把相互之间冲突的点放到一个联通快中,然后每个联通快单独计算,再乘起来就行了。

冲突定义为:两个点要么横坐标相同,要么纵坐标相同,要么同时和第三个点冲突。(也就是说一个联通块中的每个点不能自由的画线,可能画了一条线之后,会画到其他的点上去)。

考虑一个联通块内的点数和边数的关系:当只有1个点的时候,显然有2条边。那么我们如果想要加入一条边,那么这个边和已经存在的边至少要有一个交点才行,而且必须选择其中至少一个交点 加入到点集中(边依附点而存在,想加一个边,必须找到他依附的点才行),也就是说,边数每次+1,点数至少+1(可以在边数不增加的前提下,增加点数,比如井字图,四条边,可以有两个三个四个点都可以)。那么归纳一下就是说 边数<=点数+1。

一:边数==点数+1 (设x+1边,x点)而每个点最多只能画一条边,那么最多只能画出x个边,x+1个边同时出现这种图案是非法的。其他所有的图案都是合法的。这个好像可以构造一个二分图来简单证明一下,或者自己模拟模拟也可以看出来。所以这个联通快可以实现所有0边的、1边的、2边的.....x边的图案。也就是C(x+1,0)+C(x+1,1)+C(x+1,2)+......+C(x+1,x)=2^(x+1)-1

二:边数==点数(x边,x点),由于上面的讨论,可以得知所有x边的组合都可以实现,那么就可以得到2^x种图案。

三:边数<点数(x边,y点,x<y),同理,x边的所有组合都可以实现,可以得到2^y种图案

总结:统计每个联通块的点数和边数,如果 边数==点数+1,那么答案贡献是2^边数-1。否则答案贡献是2^边数。剩下的就是并查集操作了。

#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<sstream>
#include<cstdlib>
#include<time.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define repp(i,n,a) for(int i=n;i>=a;i--)
#define ll long long
#define ull unsigned long long
#define mem(a,x) memset((a),(x),sizeof ((a)))//x只能是0或-1或false或true
#define debug(x) cout<<"X: "<<(x)<<endl
#define de cout<<"************"<<endl
#define lowbit(x) ((x)&(-x))
#define lson rt<<1
#define rson rt<<1|1
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a*b/(__gcd(a,b))
#define inf 0x3f3f3f3f//1e9+6e7
#define Eps 1e-8
#define Mod 1000000007
#define maxn 100010
const double pi=acos(-1.0);
using namespace std;
map<int,int> hsx,hsy;
int x[200010],y[200010];
int fa[200010];
int findfa(int x)
{
    if(fa[x]==x)
        return x;
    else
        return fa[x]=findfa(fa[x]);
}
ll pow_mod(ll a,ll b,ll mod)
{
    ll ret=1;
    while(b)
    {
        if(b&1)
            {ret=(ret*a)%mod;b--;}
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}
int b[200010],d[200010];    //边数    点数
int main()
{
    int n;
    cin>>n;
    int cnt=0;
    rep(i,0,n-1)
    {
        scanf("%d%d",&x[i],&y[i]);
        if (!hsx.count(x[i]))
            hsx.insert(make_pair(x[i],cnt++));
        if (!hsy.count(y[i]))
            hsy.insert(make_pair(y[i],cnt++));

    }
    rep(i,0,cnt-1)
        fa[i]=i;
    rep(i,0,n-1)
        fa[findfa(hsx[x[i]])]=findfa(hsy[y[i]]);

    rep(i,0,n-1)
        d[findfa(hsx[x[i]])]++;
    rep(i,0,cnt-1)
        b[findfa(i)]++;

    ll ans=1;
    rep(i,0,cnt-1)
    {
        if(fa[i]==i)
        {
            if(b[i]>d[i])
                ans=ans*(pow_mod(2,b[i],(ll)(Mod))-1)%Mod;
            else
                ans=ans*pow_mod(2,b[i],(ll)(Mod))%Mod;
        }
    }
    cout<<ans;
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值