题目链接:
Problem - C - Codeforceshttps://codeforces.com/contest/1039/problem/C
题意:
计算机网络由个服务器组成,每个服务器有到范围内的加密秘钥。设是分配给第i台服务器的加密密钥。对服务器通过数据通信通道直接连接。由于加密算法的特殊性,只有它连接的两台服务器具有不同的加密秘钥时,数据通信通道才能被认为是安全的。一种包含未知数字的病毒在传播,当它感染服务器时,加密密钥会从变成(为异或)。
找到的对数,其中是服务器集合的某个(可能是空的)子集,是到范围内的某个数字,这样当所选子集中的所有服务器和其他服务器都没有被包含数字的病毒感染时,所有数据通信通道都保持安全。答案可能相当大,将结果。
分析:
这题面很长,需要转译,问题可以转为:问有多少,使得当所有中的权值都变为(为异或操作),仍然保证条边都是安全的。
分类讨论:
考虑一条边的两个端点,它们两点对应的权值为,因为,,即对于某条边,病毒同时感染或者同时不感染这条边是没事的。
而当且仅当时,感染一个点会出现不合法的情况。
于是可以对每条边设置一个权值。若某个病毒的权值为,那么合法感染点有形成的连通块的个数。
没出现的,合法感染点有种方案。
对连通块的解释:
无其他限制时,对于任意的,的个数都是,考虑一条边,若,那么和就会被绑定,从而形成连通块,这两个点要不然都在集合中,要不然都不在集合中。对每一个这样的,我们能将和连边,用并查集求出连通块的个数。设连通块个数为,对于这个,对应的的个数为个,且任意。对相同进行分解,答案是
成立的个数如何计算?代码有注释。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define fi first
#define se second
constexpr LL mod=1e9+7;
constexpr int N=5e5+30;
LL c[N];
LL fa[N];
LL n,m,k,cnt,num,ans;
set<LL> s;
void YES(){
cout<<"YES"<<"\n";
}
void NO(){
cout<<"NO"<<"\n";
}
struct Edge{
LL u,v,w;
}e[N];
bool cmp(Edge xx,Edge yy){
return xx.w<yy.w;
}
LL Find(LL x){
return x==fa[x]?x:fa[x]=Find(fa[x]);
}
LL qpow(LL x,LL y){return y?(y&1?x*qpow(x,y-1)%mod:qpow(x*x%mod,y/2)):1;}
void add(LL u,LL v){
s.insert(u);
s.insert(v);
if(Find(u)!=Find(v)) fa[Find(u)]=Find(v),cnt--;
return ;
}
void work(){
cin>>n>>m>>k;
for(LL i=1;i<=n;++i) cin>>c[i];
for(LL i=1;i<=n;++i) fa[i]=i;
for(LL i=1;i<=m;++i){
cin>>e[i].u>>e[i].v;
e[i].w=c[e[i].u]^c[e[i].v];
}
sort(e+1,e+m+1,cmp);
for(LL i=1;i<=m;){
LL j=i;
cnt=n;
for(auto x:s) fa[x]=x;s.clear();
while(j<=m&&e[j].w==e[i].w){
add(e[j].u,e[j].v);
j++;
}
i=j;
num++;//num即为成立个数
ans+=qpow(2,cnt);
ans%=mod;
}
cout<<1ll*((ans+(qpow(2,k)+mod-num)*qpow(2,n)%mod)%mod)<<"\n";
return ;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
work();
return 0;
}