题解:CF870E Points, Lines and Ready-made Titles
根据乘法原理可得:几个独立的整体组成的图案个数为各个整体组成的方案数做乘积。
如何判断是否是一个独立的整体呢?
考虑反向思考:若两个部分互相干扰,则它们是一个整体。而干扰当且仅当两个部分至少有一个点共线于平行于垂直线或平行线。
我们发现这个东西好像可以用并查集维护。将各个边维护在一起,若两条边有交点则它们是整体。此时对于每一个给出的点即为交点。
接下来最后的问题:独立的整体的方案数。
每一条边的形态由上一条决定,若形成环,则方案数为 \(2^{size}\),其中 \(size\) 是该整体的边的数量。若为一棵树则方案数为 \(2^{size}-1\)。
看了一下有的题解没有很清楚地把这个 \(2^{size}-1\) 解释清楚。我们可以构造:
3
0 0
0 1
1 0
和
4
0 0
0 1
1 0
1 1
进行对比。画出来可以发现后者较于前者多一个 #
型的图案。可以推广,树形总是比环少存在这样一个 #
型。
code:
int n;
int point;
int f[1000086];
int size[1000086];
bool h[1000086];
int find(int x){
return f[x]==x?x:f[x]=find(f[x]);
}
void add(int x,int y){
int fax=find(x);
int fay=find(y);
if(fax==fay)
h[fax]=true;
else{
f[fax]=fay;
size[fay]+=size[fax];
if(h[x])
h[y]=true;
}
return;
}//并查集模板
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans%mod;
}//快速幂
class node{
public:
int x;
int y;
}a[1000086];
int ans=1;
std::map<int,int>mp1,mp2;
signed main(){
std::cin>>n;
for(int i=1;i<=n;i++){
std::cin>>a[i].x>>a[i].y;
if(not mp1[a[i].x])
mp1[a[i].x]=++point;
if(not mp2[a[i].y])
mp2[a[i].y]=++point;//编号
}
for(int i=1;i<=point;i++){
f[i]=i;
size[i]=1;
}//ini
for(int i=1;i<=n;i++)
add(mp1[a[i].x],mp2[a[i].y]);//交点合并
for(int i=1;i<=point;i++)
if(f[i]==i)
if(h[i])//环
ans=ans*qpow(2,size[i])%mod;
else
ans=ans*(qpow(2,size[i])-1)%mod;
else;
std::cout<<ans;
return 0;//撒花
}