http://acm.hdu.edu.cn/showproblem.php?pid=6411
思路:
先用并查集把各个联通块整理出来,再把每个联通块点的权值按照从小到大排序,因为是&,所以只有同时满足在这个位置为1的去情况才为1,可以统计每个位置1的个数,从大到小扫一遍就可以了,记住每次都要删去扫过的,不然会重复
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int p[maxn];
int n,m;
int w[maxn];
vector<int>e[maxn];
void init()
{
for(int i=0;i<=n;i++)
p[i]=i,e[i].clear();
}
int findx(int x)
{
if(x==p[x])return x;
return p[x]=findx(p[x]);
}
void uion(int x,int y)
{
int xx=findx(x);
int yy=findx(y);
if(xx!=yy)
{
p[yy]=xx;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
}
int u,v;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
uion(u,v);
}
for(int i=1;i<=n;i++)
{
e[findx(i)].push_back(w[i]);
}
ll ans=0;
for(int i=1;i<=n;i++)
{
sort(e[i].begin(),e[i].end());
int len=e[i].size();
int cnt[35]={0};
for(int j=0;j<len;j++)
{
int t=e[i][j];
for(int k=0;k<32&&t;k++)
{
if(t&1)
cnt[k]++;
t>>=1;
}
}
for(int j=len-1;j>0;j--)
{
int t=e[i][j];
for(int k=0;k<32&&t;k++)
{
if(t&1)
{
cnt[k]--;
ans=(ans+(1LL<<k)%mod*cnt[k]%mod*e[i][j]%mod)%mod;
}
t>>=1;
}
}
}
printf("%lld\n",ans);
}
return 0;
}