带劲的and和
Accepts: 620
Submissions: 1669
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
Problem Description
度度熊专门研究过“动态传递闭包问题”,他有一万种让大家爆蛋的方法;但此刻,他只想出一道简简单单的题——至繁,归于至简。
度度熊有一张n个点m条边的无向图,第ii个点的点权为v_ivi。
如果图上存在一条路径使得点ii可以走到点jj,则称i,ji,j是带劲的,记f(i,j)=1f(i,j)=1;否则f(i,j)=0f(i,j)=0。显然有f(i,j) = f(j,i)f(i,j)=f(j,i)。
度度熊想知道求出: \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} f(i,j) \times \max(v_i, v_j) \times (v_i \& v_j)∑i=1n−1∑j=i+1nf(i,j)×max(vi,vj)×(vi&vj)
其中\&&是C++中的and位运算符,如1&3=1, 2&3=2。
请将答案对10^9+7109+7取模后输出。
Input
第一行一个数,表示数据组数TT。
每组数据第一行两个整数n,mn,m;第二行nn个数表示v_ivi;接下来mm行,每行两个数u,vu,v,表示点uu和点vv之间有一条无向边。可能有重边或自环。
数据组数T=50,满足:
- 1 \le n,m \le 1000001≤n,m≤100000
- 1 \le v_i \le 10^91≤vi≤109。
其中90%的数据满足n,m \le 1000n,m≤1000。
Output
每组数据输出一行,每行仅包含一个数,表示带劲的and和。
Sample Input
1 5 5 3 9 4 8 9 2 1 1 3 2 1 1 2 5 2
Sample Output
Copy
99
思路:
分别计算每一个联通块内的值,可以直接bfs。计算时可以利用二进制的位进行运算。先排序,从前到后扫一遍,用一个bin数组记录每一个二进制位的1的出现次数,判断下一个值二进制中1的位置,然后从bin中相应位置取出运算就可以了。复杂度O(31*n)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 100005
#define inf 0x3f3f3f3f3f3f3f3f
ll const mod = 1e9+7;
int n,m;
int bin[35];
ll val[MAXN],buf[MAXN];
vector<int> vec[MAXN];
bool vis[MAXN];
ll bfs(int st)
{
int tot=0;
queue<int> q;
q.push(st);
vis[st]=1;
buf[tot++]=val[st];
while(!q.empty())
{
int top=q.front(); q.pop();
for(int i=0;i<vec[top].size();i++)
{
int v=vec[top][i];
if(!vis[v])
{
vis[v]=1;
buf[tot++]=val[v];
q.push(v);
}
}
}
sort(buf,buf+tot);
ll ret=0;
memset(bin,0,sizeof bin);
for(int i=0;i<tot-1;i++)
{
ll s=0;
for(int j=0;buf[i];j++)
{
if(buf[i]&1) bin[j]++;
buf[i]>>=1;
}
ll k=buf[i+1];
for(int j=0;k;j++)
{
if(k&1)
{
s=(s+(ll)bin[j]*(1<<j))%mod;
}
k>>=1;
}
ret=(ret+buf[i+1]*s)%mod;
}
return ret;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",val+i);
for(int i=1;i<=n;i++)
vec[i].clear();
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
vec[u].push_back(v);
vec[v].push_back(u);
}
memset(vis,0,sizeof vis);
ll ans=0;
for(int i=1;i<=n;i++)
{
if(!vis[i]) ans=(ans+bfs(i))%mod;
}
printf("%I64d\n",ans);
}
return 0;
}