带劲的and和
Accepts: 781
Submissions: 2382
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
Problem Description
度度熊专门研究过“动态传递闭包问题”,他有一万种让大家爆蛋的方法;但此刻,他只想出一道简简单单的题——至繁,归于至简。
度度熊有一张n个点m条边的无向图,第iii个点的点权为viv_ivi。
如果图上存在一条路径使得点iii可以走到点jjj,则称i,ji,ji,j是带劲的,记f(i,j)=1f(i,j)=1f(i,j)=1;否则f(i,j)=0f(i,j)=0f(i,j)=0。显然有f(i,j)=f(j,i)f(i,j) = f(j,i)f(i,j)=f(j,i)。
度度熊想知道求出: ∑i=1n−1∑j=i+1nf(i,j)×max(vi,vj)×(vi&vj)\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。
请将答案对109+710^9+7109+7取模后输出。
Input
第一行一个数,表示数据组数TTT。
每组数据第一行两个整数n,mn,mn,m;第二行nnn个数表示viv_ivi;接下来mmm行,每行两个数u,vu,vu,v,表示点uuu和点vvv之间有一条无向边。可能有重边或自环。
数据组数T=50,满足:
- 1≤n,m≤1000001 \le n,m \le 1000001≤n,m≤100000
- 1≤vi≤1091 \le v_i \le 10^91≤vi≤109。
其中90%的数据满足n,m≤1000n,m \le 1000n,m≤1000。
Output
每组数据输出一行,每行仅包含一个数,表示带劲的and和。
Sample Input
Copy
1
5 5
3 9 4 8 9
2 1
1 3
2 1
1 2
5 2
Sample Output
Copy
99
Statistic | Submit | Clarifications | Back
题解:首先,能互相到达的一定在一个集合里,用并查集搞一搞,之后对于每个集合从小到大排序,然后对于每个点求其贡献即可。
#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll long long
#define mod 1000000007
int n,m;
ll a[100005],p[100005],sum[40],ans;
vector<ll>q[100005];
int find(int x)
{
if(p[x]==x)
return x;
return p[x]=find(p[x]);
}
int main(void)
{
int T,x,y;
scanf("%d",&T);
while(T--)
{
ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
q[i].clear();
for(int i=1;i<=n;i++)
p[i]=i;
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
int t1=find(x),t2=find(y);
if(t1!=t2) p[t1]=t2;
}
for(int i=1;i<=n;i++)
q[find(i)].push_back(a[i]);
for(int i=1;i<=n;i++)
sort(q[i].begin(),q[i].end());
for(int i=1;i<=n;i++)
{
if(q[i].size()<=1)
continue;
memset(sum,0,sizeof(sum));
for(int j=0;j<q[i].size();j++)
{
for(ll k=0;k<32;k++)
{
if((q[i][j]&(1<<k)))
ans=(ans+q[i][j]*(sum[k]*(ll)(1<<k)%mod)%mod)%mod,sum[k]++;
}
}
}
printf("%lld\n",ans);
}
return 0;
}