Codeforces Round #507 (Div. 2, based on Olympiad of Metropolises) E. Network Safety(异或运算+SCC)

E. Network Safety

题意:

输入 n ( 5 e 5 ) , m ( m i n ( n × ( n − 1 ) 2 , 5 e 5 ) ) , k ( 60 ) n(5e5),m(min(\frac {n\times (n-1)} 2,5e5)),k(60) n(5e5),m(min(2n×(n1),5e5)),k(60)
接下来一行输入 a 1 , a 2 , … , a n ( [ 0 , 2 k ) ) a_1,a_2,\dots,a_n([0,2^k)) a1,a2,,an([0,2k))
接下来 m m m行输入 u , v u,v u,v,表示边,保证 a u ≠ a v a_u\neq a_v au=av
问有多少个 ( A , x ) (A,x) (A,x), A A A是此图的点集(可以为空), x ( [ 0 , 2 k ) ) x([0,2^k)) x([0,2k)),使 x x x A A A中每个点点值异或后,每条边两端的点值依旧不同。

题解:

首先考虑对于每个值 x x x分析,求有多少个子集符合要求。
a u ≠ a v 所 以 a u ⨁ x ≠ a v ⨁ x a_u\neq a_v 所以a_u\bigoplus x\neq a_v\bigoplus x au=avaux=avx
于是便可以分两种情况:

  • a u ⨁ x ≠ a v a_u\bigoplus x\neq a_v aux=av时, a v a_v av有两种选择。
  • a u ⨁ x = a v a_u\bigoplus x=a_v aux=av时, a v a_v av只有一种选择。

因此可以把所有的 a u ⨁ a v = x a_u\bigoplus a_v=x auav=x u , v u,v u,v都拿出来,形成一种子图,求联通分量的个数 g g g,把 2 g 2^g 2g加入答案即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod=1e9+7;
const int N=5e5+9;
ll n,m,k,a[N];
map<ll,int>mp;
int cnt;
#define P pair<int,int>
P b[N];
vector<P>g[N];
ll ans;
int poww(int a,int b,int c){
    int ans=1,base=a;
    while(b){
        if(b&1)ans=1ll*ans*base%c;
        base=1ll*base*base%c;
        b>>=1;
    }
    return ans;
}
int solve1(){
    return 1ll*poww(2,n,mod)*(((1ll<<k)-cnt)%mod)%mod;
}
int f[N],siz[N];
int findf(int x){return f[x]==x?x:f[x]=findf(f[x]);}
int solve2(int x){
    set<int>s;
    for(auto i:g[x]){
        int fx=findf(i.first),fy=findf(i.second);
        if(fx!=fy){
            f[fx]=fy,siz[fy]+=siz[fx];
        }
    }
    int sum=0,sz=0;
    for(auto i:g[x]){
        if(f[i.first]==i.first&&!s.count(i.first))sum++,sz+=siz[i.first],s.insert(i.first);
        if(f[i.second]==i.second&&!s.count(i.second))sum++,sz+=siz[i.second],s.insert(i.second);
    }
    s.clear();
    for(auto i:g[x]){
        f[i.first]=i.first,f[i.second]=i.second;
        siz[i.first]=siz[i.second]=1;
    }
    return poww(2,n-sz+sum,mod);
}
int main(){
   // freopen("tt.in","r",stdin),freopen("tt.out","w",stdout);
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=m;i++)cin>>b[i].first>>b[i].second;
    for(int i=1;i<=m;i++){
        ll t=a[b[i].first]^a[b[i].second];
        if(!mp.count(t))mp[t]=++cnt;
        g[mp[t]].push_back(b[i]);
    }
    ans=solve1();
    for(int i=1;i<=n;i++)f[i]=i,siz[i]=1;
    for(int i=1;i<=cnt;i++)ans=(ans+solve2(i))%mod;
    cout<<ans<<endl;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值