CF#507-C-Network Safety-題解
阿新 • • 發佈:2018-12-19
題目地址-CF
- 簡略題意
給你一個 個點的圖, 條邊,每個點有個點權 ,再給你一個 ,對於一個 和一個點集 ,將 中的所有點的點權異或 ,然後對於改變點權後的原圖如果沒有任何一條邊兩端端點的點權一樣,那麼稱這個 是合法的,問你總共有多少合法的 , 可以為空集,保證一開始給定的圖沒有一條邊兩端端點值一樣。
其實我們令一條邊的邊權為 ,那麼肯定對於這條邊的兩個端點,要麼都異或 ,要麼都不異或,所以對於一個邊權都為 的一個連通塊,它只有兩種選擇,要麼都異或 ,要麼都不異或 。所以我們將邊按照邊權排序,然後對於同一種邊權,它的方案數就為 ( 為連通塊大小, 為當前連通塊個數, 為當前在連通塊內的點的個數),也就是連通塊的選擇方案數乘以其它點的選擇方案數(異不異或只有兩種狀態所以為 的多少次方)。然後對於其它不為邊權的 ,方案數就是 ,加上即可。
#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=5e5+10;
const ll Mod=1e9+7;
ll n,m,k;
ll C[M],ans;
set <int> rec,kua;//自動去重
int f[M];
int find(int a){return f[a]==a?a:f[a]=find(f[a]);}
void merge(int a,int b){
rec.insert(a);rec.insert(b);
a=find(a);b=find(b);
if(a!=b)f[a]=b;
}
ll fpow(ll a,ll b){
ll ans=1;
for(;b;b>>=1,a=(a*a)%Mod){if(b&1)ans=(ans*a)%Mod;}
return ans;
}
struct edge{
int u,v;ll w;
void in(){scanf("%d%d",&u,&v);w=C[u]^C[v];}
edge(){}
edge(int a,int b,ll c):u(a),v(b),w(c){}
bool operator <(const edge &a)const{return w<a.w;}
}E[M];
void turn_back(){
for(auto a:rec)f[a]=a;rec.clear();//還原要暫存,否則複雜度變成O(nm)了
kua.clear();
}
int main(){
scanf("%I64d%I64d%I64d",&n,&m,&k);
for(int i=1;i<=n;i++)scanf("%I64d\n",&C[i]),f[i]=i;
for(int i=1;i<=m;i++)E[i].in();
sort(E+1,E+m+1);
ll last=0,All=fpow(2ll,n);
for(int i=1,j;i<=m;){
ans=(ans+All*((E[i].w-last)%Mod)%Mod)%Mod;//不為邊權的x的貢獻
last=E[i].w;
for(j=i;j<=m&&E[j].w==last;j++){merge(E[j].u,E[j].v);}
for(j=i;j<=m&&E[j].w==last;j++){
int a=find(E[j].u),b=find(E[j].v);
kua.insert(a);kua.insert(b);
}
i=j;
//rec.size()為連通塊內點數,kua.size()為連通塊個數
ans=(ans+(fpow(2ll,n+kua.size()-rec.size())%Mod))%Mod;
turn_back();++last;
}
ans=(ans+All*((fpow(2ll,k)-last)%Mod+Mod)%Mod)%Mod;
printf("%I64d\n",ans);
return 0;
}