1. 程式人生 > >複賽-C-1003-帶勁的and和(並查集+按位貢獻)

複賽-C-1003-帶勁的and和(並查集+按位貢獻)

帶勁的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_iv​i​​。

如果圖上存在一條路徑使得點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=1​n−1​​∑​j=i+1​n​​f(i,j)×max(v​i​​,v​j​​)×(v​i​​&v​j​​)

其中&\&&是C++中的and位運算子,如1&3=1, 2&3=2。

請將答案對109+710^9+710​9​​+7取模後輸出。

Input

第一行一個數,表示資料組數TTT。

每組資料第一行兩個整數n,mn,mn,m;第二行nnn個數表示viv_iv​i​​;接下來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≤v​i​​≤10​9​​。

其中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

題解:首先,能互相到達的一定在一個集合裡,用並查集搞一搞,之後對於每個集合從小到大排序,然後對於每個點求其貢獻即可。

#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;
}