原题链接:
hdu
题意简述
给定一个无向图,点数1e5,点权1e9,设
f
(
u
,
v
)
f(u,v)
f(u,v)表示:
u
,
v
u,v
u,v能否联通(珂以间接)。
求:对于任意
u
,
v
u,v
u,v,加和
f
(
u
,
v
)
∗
(
u
&
v
)
∗
m
a
x
(
u
,
v
)
f(u,v)*(u\&v)*max(u,v)
f(u,v)∗(u&v)∗max(u,v)。
思路
和上一篇类似的求和风格。首先,我们要知道啥时候 f ( u , v ) = 1 f(u,v)=1 f(u,v)=1,就是 u , v u,v u,v一个联通块里面。考虑使用并查集维护联通块,然后把每个联通块里的点存到 v e c t o r < i n t > vector<int> vector<int>中。
接着就是求后面了。 m a x ( u , v ) max(u,v) max(u,v)的思路已经知道了,我们对于每个联通块的元素排序,枚举 i i i,去找 i i i和前面的答案。这样就能保证 m a x ( u , v ) = i max(u,v)=i max(u,v)=i了。
接下来就是要求前面所有元素和 i i i的 a n d and and和。如何求这个 a n d and and和呢?我的老师教导我,遇到这种位运算的东西,就按位求答案。我们设 c n t [ k ] cnt[k] cnt[k]表示:前面的数中,多少个数二进制第 k k k位是 1 1 1。
注释(如果不想看,大可跳过):
x x x的第 y y y位是 1 1 1被这么定义: ( ( x > > y ) & 1 ) = = 1 ) ((x>>y)\&1)==1) ((x>>y)&1)==1)
那么,当我们枚举到 i i i的第 j j j位的时候,如果 i i i有第 j j j位,就会有 c n t [ j ] cnt[j] cnt[j]个数和 i i i取 a n d and and时,在第 j j j位上有答案。此时对答案的贡献就是 ( 1 < < j ) ∗ c n t [ j ] (1<<j)*cnt[j] (1<<j)∗cnt[j]。
然后这样枚举就好了。复杂度是
O
(
32
∗
n
)
O(32*n)
O(32∗n)。
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define N 100100
#define mod 1000000007ll
#define ll long long
#define MEM(x,a) memset(x,a,sizeof(x))
#define CLS(x) memset(x,0,sizeof(x))
class DSU//并查集
{
public:
int Father[N],Cnt[N];
void Init()
{
for(int i=0;i<N;i++)
{
Father[i]=i;
Cnt[i]=1;
}
}
int Find(int x)
{
return (x==Father[x])?x:(Father[x]=Find(Father[x]));
}
void Merge(int x,int y)
{
int ax=Find(x),ay=Find(y);
Father[ax]=ay;
}
}D;
int a[N];
int n,m;
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
void Input()
{
R1(n),R1(m);
D.Init();
for(int i=1;i<=n;++i)
{
R1(a[i]);
}
for(int i=1;i<=m;++i)
{
int u,v;
R1(u),R1(v);
D.Merge(u,v);
}
}
vector<int>nodes[N];
int cnt[32];//cnt[i]:how many (1<<i) appeared previously
//我习惯在写代码的时候加英文注释,懒得删了
void Soviet()
{
for(int i=1;i<=n;++i)//记得清空
{
nodes[i].clear();
}
for(int i=1;i<=n;++i)//枚举并查集,并保存好每个vector
{
int an=D.Find(i);
nodes[an].push_back(a[i]);
}
ll ans=0;
for(int i=1;i<=n;++i)
{
if (nodes[i].size()==0) continue;
CLS(cnt);//每次都要初始化!!!
sort(nodes[i].begin(),nodes[i].end());//排序,方便处理max
for(int j=0;j<nodes[i].size();++j)
{
int val=nodes[i][j];
for(int k=0;k<32;++k)//枚举每一位,记录cnt值
{
if (val==0) break;
if (val&1) ++cnt[k];
val>>=1;
}
}
for(int j=nodes[i].size()-1;j>=0;--j)
{
int val=nodes[i][j];
int tmp=val;
for(int k=0;k<32;++k)
{
if (tmp==0) break;
if (tmp&1)
{
--cnt[k];
ll sum=1;
sum*=val;sum%=mod;
sum*=(1ll<<k);sum%=mod;
sum*=cnt[k];sum%=mod;
ans+=sum;ans%=mod;
//ans*=val*(1<<k)*cnt[k];
}
tmp>>=1;
}
}
}
ans=(ans%mod+mod)%mod;
printf("%lld\n",ans);
}
void IsMyWife()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
int t;R1(t);
while(t--)
{
Input();
Soviet();
}
}
};
int main()
{
Flandle_Scarlet::IsMyWife();
return 0;
}