度度熊專門研究過「動態傳遞閉包問題」,他有一萬種讓大家爆蛋的方法;但此刻,他只想出一道簡簡單單的題——至繁,歸於至簡。
度度熊有一張n個點m條邊的**無向圖**,第ii個點的點權為vivi。
如果圖上存在一條**路徑**使得點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)。
度度熊想知道求出:
∑n−1i=1∑nj=i+1f(i,j)×max(vi,vj)×(vi&vj)∑i=1n−1∑j=i+1nf(i,j)×max(vi,vj)×(vi&vj)
其中&&是c++中的and位運算子,如1&3=1, 2&3=2。
請將答案對109+7109+7取模後輸出。
input
第一行乙個數,表示資料組數tt。
每組資料第一行兩個整數n,mn,m;第二行nn個數表示vivi;接下來mm行,每行兩個數u,vu,v,表示點uu和點vv之間有一條無向邊。**可能有重邊或自環。**
資料組數t=50,滿足:
- 1≤n,m≤1000001≤n,m≤100000
- 1≤vi≤1091≤vi≤109。
其中90%的資料滿足n,m≤1000n,m≤1000。
output
每組資料輸出一行,每行僅包含乙個數,表示帶勁的and和。
sample input
1sample output5 53 9 4 8 9
2 11 3
2 11 2
5 2
99題意:中文題嘛....其實就是找相互連通的頂點 的公式和(兩者之間的最大值 * 兩者的 & 運算值 求和)。
思路:很容易想到用並查集找到相互連通的頂點。之後就是在集合裡運算了...直接暴力求和?結果當然是tle了。
那麼我們來換一種思路:在這裡,我們是要求 兩者之間的最大值 * 兩者的 & 運算值 求和;對於最大值,我們可以從小到大排一下序,那麼此時我們求的就是:(好醜...)
接下來我們再看十進位制乘二進位制 乘法運算emmmm....orz....
接下來我們再看 & 運算emmmm....orz....
好了,重點來了!!!!因為我們先對集合內的元素進行排序,公式可以轉化成圖一那樣:也就是說對於第n個數,它要乘以
(1--> n-1 中每一數和它 & 的和)。那麼我們就可以開乙個dp陣列記錄,到達第n個數之前,每一位(二進位制)出現的次數,因為 &運算中(兩個都1 結果才為 1),如果第n個數的第 k 位(二進位制)為 1 的話,我們就需要看看前面的那些數中有沒有第k位為1的數,假設有dp[k]個,那麼這個數(xn)就可以利用十進位制乘二進位制求和了: (xn * dp[k] * (1**如下:
#include#include#include#include#define n 100010
#define ll long long
using namespace std;
const int mod = 1e9+7;
int n,f[n],a[n];
vectorv[n];
void init()
}int getf(int x)
void merge(int x,int y)
int main()
for(int i=1; i<=n; i++)
ll sum=0,dp[50];
for(int i=1; i<=n; i++) }}
}}printf("%lld\n",sum);}}
HDU 6411 帶勁的and和 並查集 位運算
題目鏈結 略n 1e5的資料規模肯定是不能直接列舉兩個相連的點的。既然題目中用到了位運算,就要從二進位制的角度來考慮。對於乙個連通分量,我們將其中的所有點按照權值排序,這時候再暴力列舉的話,肯定是最大的點和其他點分別來做與運算,然後次大的點和比它小的點做與運算。從位運算的角度思考,只有兩個數相同二進...
並查集 優秀的找爹模板題
並查集的操作有三步,初始化,查詢祖先與合併。既然並查集是來查詢祖先的,那麼初始化就必然是讓每個點的祖先指向自己 for int i 1 i n i fa i i 查詢操作就是不斷地向上走,直到找到祖先為止 while x fa x x fa x 合併操作就是把乙個節點的祖先變為另乙個節點的祖先。fa...
並查集判斷環 並查集的路徑壓縮 和 帶秩優化
1.判斷環 參考部落格 思路 1.將用過的路徑連起來成為乙個集合,記錄下來 2.如果連通的兩個邊屬於乙個集合,那麼這個並查集就形成了乙個環 燈神 如果刪除2,4邊 可將2,4這條邊刪除測試 是否正確 如果刪除此邊則不會出現環記得將6改為5 initialise parent for int i 0 ...