题目链接:传送门
带劲的and和
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 219 Accepted Submission(s): 113
Problem Description
度度熊专门研究过“动态传递闭包问题”,他有一万种让大家爆蛋的方法;但此刻,他只想出一道简简单单的题——至繁,归于至简。
度度熊有一张n个点m条边的**无向图**,第i个点的点权为vi。
如果图上存在一条**路径**使得点i可以走到点j,则称i,j是**带劲**的,记f(i,j)=1;否则f(i,j)=0。显然有f(i,j)=f(j,i)。
度度熊想知道求出:
(^表示&)其中&是C++中的and位运算符,如1&3=1, 2&3=2。
请将答案对109+7取模后输出。
Input
第一行一个数,表示数据组数T。
每组数据第一行两个整数n,m;第二行n个数表示vi;接下来m行,每行两个数u,v,表示点u和点v之间有一条无向边。**可能有重边或自环。**
数据组数T=50,满足:
- 1≤n,m≤100000
- 1≤vi≤109。
其中90%的数据满足n,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
99
解题思路:
1.对于f(i,j),若i,j在图中的联通块内,则f(i,j)=1,所以一个一个联通块来计算表达式的值。
2.对于max(vi,vj),枚举一个联通块中结点的最大值,对结点数值排序,这样能快速计算有多少对结点的max(vi,vj)为我们当前枚举的对象。
3.对于(vi&vj),从第二步我们枚举了一个数值vx,我们只需要计算max(vi,vj) = vx的结点对中对vx二进制位为1的贡献,举个例子。对于max(3,5) = 5,5的二进制表示为101,3的二进制表示为11,则3对5的首位贡献为1。之前对联通块中结点的数值排了序,这里从小到大计算结点对数对枚举的vx的贡献。
时间复杂度为0(nlogn+30*n)
代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#include <ctime>
using namespace std;
typedef long long llt;
const int INF = 0x3fffffff;
const int N = 100010;
const double pi = acos(-1);
const int mod = 1e9+7;
struct Edge{
int node;
Edge*next;
}edge[N*2];
Edge*head[N];
int Ecnt,vis[N],val[N],bia[40];
vector<llt>vec;
void init()
{
Ecnt = 0;
memset(vis,0,sizeof(vis));
fill(head,head+N*2,(Edge*)0);
}
void mkEdge(int a,int b)
{
edge[Ecnt].node = b;
edge[Ecnt].next = head[a];
head[a] = edge+Ecnt++;
}
void dfs(int u)
{
vis[u] = 1;
vec.push_back(val[u]);
for(Edge*p = head[u]; p; p = p->next){
int v = p->node;
if(vis[v]) continue;
dfs(v);
}
}
llt solve()
{
memset(bia,0,sizeof(bia));
sort(vec.begin(),vec.end());
llt ans = 0;
for(int i = 0; i < vec.size(); ++i){
int cnt = 0; llt temp = vec[i];
while(temp){
if(temp&1){
llt a = bia[cnt],b = 1<<cnt;
ans = (ans+vec[i]*a%mod*b%mod)%mod;
bia[cnt]++;
}
temp >>= 1;
cnt++;
}
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
init();
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; ++i) scanf("%d",&val[i]);
int a,b;
for(int i = 0; i < m; ++i){
scanf("%d%d",&a,&b);
mkEdge(a,b);
mkEdge(b,a);
}
llt ans = 0;
for(int i = 1; i <= n; ++i){
if(vis[i]) continue;
vec.clear();
dfs(i);
ans = (ans+solve())%mod;
}
printf("%lld\n",ans);
}
return 0;
}