【訓練題25:數學+位運算】E : Apollo versus Pan | CF Good Bye 2020
E : Apollo versus Pan | CF Good Bye 2020
題外話
有可能是太晚了太困了,或者是按位運算的數學太差了///
做了好久都沒有起色,於是草草睡覺了
第二天補一下題
難度
− 3370 14603 -\frac{3370}{14603} −146033370
題意
給你一個長度為
n
n
n 的序列
x
n
x_n
xn
讓你求
∑
i
=
1
n
∑
j
=
1
n
∑
k
=
1
n
(
x
i
&
x
j
)
×
(
x
j
∣
x
k
)
\sum_{i=1}^n\sum_{j=1}^n\sum_{k=1}^n(x_i\&x_j)\times(x_j|x_k)
i=1∑nj=1∑nk=1∑n(xi&xj)×(xj∣xk)
答案取模
1
e
9
+
7
1e9+7
1e9+7
其中
&
\&
& 是按位與運算 ,
∣
|
∣ 是 按位或運算。
資料範圍
0
≤
x
i
≤
2
60
0\le x_i\le 2^{60}
0≤xi≤260
1
≤
n
≤
5
×
1
0
5
1\le n\le 5\times10^5
1≤n≤5×105
思路
-
這種按位運算的題,大概率都是按位處理的。
(大概) -
但是先要處理一下式子,不然只能 O ( N 3 ) O(N^3) O(N3) 硬算不實際。
-
原 式 = ∑ i = 1 n ∑ j = 1 n ∑ k = 1 n ( x i & x j ) × ( x j ∣ x k ) = ∑ j = 1 n ∑ i = 1 n ∑ k = 1 n ( x i & x j ) × ( x j ∣ x k ) = ∑ j = 1 n [ ∑ i = 1 n ( x j & x i ) ] × [ ∑ k = 1 n ( x j ∣ x k ) ] = ∑ j = 1 n F ( j ) × G ( j ) \begin{aligned}原式&=\sum_{i=1}^n\sum_{j=1}^n\sum_{k=1}^n(x_i\&x_j)\times(x_j|x_k)\\ &=\sum_{j=1}^n\sum_{i=1}^n\sum_{k=1}^n(x_i\&x_j)\times(x_j|x_k)\\ &=\sum_{j=1}^n\Bigg[\sum_{i=1}^n(x_j\&x_i)\Bigg]\times\Bigg[\sum_{k=1}^n(x_j|x_k)\Bigg]\\ &=\sum_{j=1}^n F(j)\times G(j) \end{aligned} 原式=i=1∑nj=1∑nk=1∑n(xi&xj)×(xj∣xk)=j=1∑ni=1∑nk=1∑n(xi&xj)×(xj∣xk)=j=1∑n[i=1∑n(xj&xi)]×[k=1∑n(xj∣xk)]=j=1∑nF(j)×G(j)
-
其實這題的難點就是怎麼求這兩個 F ( i ) 、 G ( i ) F(i)、G(i) F(i)、G(i) 了。
求 F ( i ) F(i) F(i)
- 容易想到,應該按位考慮。
- 對於二進位制右數第 c c c位(base為0),只有兩個數的這一位都為 1 1 1,才會對答案貢獻 2 c 2^c 2c
- 那我們計算 F ( i ) F(i) F(i) , 首先要求 x i x_i xi 的這一位必須是 1 1 1,接下來其他數有多少個數這一位是 1 1 1,就對答案貢獻幾次。
- 設 c n t c cnt_c cntc 表示有多少個數字二進位制右數第 c c c位為 1 1 1。
- 設 w c ( x i ) w_c(x_i) wc(xi)表示數字 x i x_i xi的二進位制右數第 c c c位的值(為 0 0 0 或為 1 1 1 )
- F ( i ) = ∑ c = 0 ∞ 2 c × w c ( x i ) × c n t c F(i)=\sum_{c=0}^{\infin} 2^c\times w_c(x_i)\times cnt_c F(i)=∑c=0∞2c×wc(xi)×cntc ,答案=每次貢獻價值*貢獻次數。
求 G ( i ) G(i) G(i)
這個玩意兒第一次碰到,害,也不會推,但是現在看懂了就推的套路了- 對於第 c c c 位,如果 w c ( x i ) = 1 w_c(x_i)=1 wc(xi)=1已經成立了,那麼另外的數字選擇什麼,對於答案的貢獻都是 2 c 2^c 2c
- 如果
w
c
(
x
i
)
=
0
w_c(x_i)=0
wc(xi)=0,那麼另外的數字需要選擇
w
c
(
x
j
)
=
1
w_c(x_j)=1
wc(xj)=1 的數字,才會對答案貢獻
2
c
2^c
2c
寫成數學的語言,就是兩個邏輯關係的並,寫成表示式為: - G ( i ) = ∑ c = 0 ∞ 2 c × ∑ j = 1 n 1 − ( 1 − w c ( x i ) ) ( 1 − w c ( x j ) ) = ∑ c = 0 ∞ 2 c × [ n − ( 1 − w c ( x i ) ) ∑ j = 1 n ( 1 − w c ( x j ) ) ] = ∑ c = 0 ∞ 2 c × [ n − ( 1 − w c ( x i ) ) ( n − c n t c ) ] \begin{aligned} G(i)&=\sum_{c=0}^{\infin}2^c\times\sum_{j=1}^n 1-(1-w_c(x_i))(1-w_c(x_j))\\ &=\sum_{c=0}^{\infin}2^c\times \Bigg[ n-(1-w_c(x_i))\sum_{j=1}^n(1-w_c(x_j))\Bigg]\\ &=\sum_{c=0}^{\infin}2^c\times \Bigg[ n-(1-w_c(x_i))(n-cnt_c)\Bigg] \end{aligned} G(i)=c=0∑∞2c×j=1∑n1−(1−wc(xi))(1−wc(xj))=c=0∑∞2c×[n−(1−wc(xi))j=1∑n(1−wc(xj))]=c=0∑∞2c×[n−(1−wc(xi))(n−cntc)]
- 式子再化簡開來就沒有必要了,因為 w c ( x i ) w_c(x_i) wc(xi)取值 1 1 1 或者 0 0 0 算式都很簡單。
- G ( i ) = { ∑ c = 0 ∞ 2 c × n w c ( x i ) = 1 ∑ c = 0 ∞ 2 c × c n t c w c ( x i ) = 0 G(i)=\begin{cases} \sum_{c=0}^{\infin}2^c\times n &&w_c(x_i)=1\\ \sum_{c=0}^{\infin}2^c\times cnt_c &&w_c(x_i)=0\\ \end{cases} G(i)={∑c=0∞2c×n∑c=0∞2c×cntcwc(xi)=1wc(xi)=0
其他的一些細節
- 只要預處理好 c n t i cnt_i cnti 就可以了,其他內容都可以很簡單計算得到。
- 因為 x i x_i xi最多取值到 2 60 2^{60} 260,因此式子中的 c ∈ { 0 , 1 , ⋯ , 60 } c\in\{0,1,\cdots,60\} c∈{0,1,⋯,60},複雜度並不高
- 取第
c
c
c位的程式碼應該寫成
(1LL<<j)
而不是(1<<j)
否則會上溢位
(儘管 j j j 已經是long long
型別了)
核心程式碼
時間複雜度 O ( 61 × N ) O(61\times N) O(61×N)
/*
_ __ __ _ _
| | \ \ / / | | (_)
| |__ _ _ \ V /__ _ _ __ | | ___ _
| '_ \| | | | \ // _` | '_ \| | / _ \ |
| |_) | |_| | | | (_| | | | | |___| __/ |
|_.__/ \__, | \_/\__,_|_| |_\_____/\___|_|
__/ |
|___/
*/
const int MAX = 5e5+50;
const ll MOD = 1e9+7;
ll aa[MAX];
ll cnt[100];
int main()
{
int T;cin >> T;
while(T--){
int n;scanf("%d",&n);
memset(cnt,0,sizeof(cnt));
for(int i = 1;i <= n;++i){
scanf("%lld",&aa[i]);
for(ll j = 0;j <= 60;++j){
if((1LL << j) & aa[i])cnt[j]++;
}
}
ll ans = 0;
for(int i = 1;i <= n;++i){
ll base = 1;
ll t1 = 0,t2 = 0;
for(ll j = 0;j <= 60;++j){
if((1LL << j) & aa[i]){
t1 = (t1 + base * cnt[j] % MOD) % MOD;
t2 = (t2 + base * n % MOD) % MOD;
}else{
t2 = (t2 + base * cnt[j] % MOD) % MOD;
}
base = base * 2 % MOD;
}
ans = (ans + t1 * t2 % MOD) % MOD;
}
printf("%lld\n",ans);
}
return 0;
}
相關文章
- 題解 CF997E 【Good Subsegments】Go
- Good Bye 2013Go
- CF專項訓練
- codeforces Good Bye 2014Go
- 學習位運算
- Codeforces Good Bye 2017 C. New Year and Curling(運用數學解析幾何的知識判斷)Go
- 運算整數C/C++位運算技巧C++
- 處理器運算位數
- CF938E-組合數
- CF1762F Good PairsGoAI
- CF1946E 題解
- CF1575E 題解
- CF1987E 題解
- 和演算法渣一起練習--利用位運算,輕輕鬆鬆就能解決數學裡的子集問題演算法
- 二進位制、位運算、位移運算
- (位運算)兩個字串的位運算字串
- 雲端計算時代的深度學習訓練深度學習
- 【ACM演算法競賽日常訓練】DAY16【奇♂妙拆分】【區區區間間間】【小AA的數列】數學 | 位運算 | 字首和ACM演算法
- 位運算
- CF1925D Good TripGo
- CF436E - Cardboard Box 題解
- CF1361E James and the Chase 題解
- CF466E Information Graph 題解ORM
- CF578E Walking! 題解
- CF613E Puzzle Lover 題解
- CF208E 題解
- CF571E Geometric Progressions 題解
- CF704E Iron Man 題解
- 【學校訓練記錄】10月個人訓練賽3個人題解
- acm訓練題ACM
- CF1634E Fair Share 題解AI
- CF343E Pumping Stations 題解
- CF1184E1題解
- CF712E Memory and Casinos 題解
- CF1827E Bus Routes 題解
- CF1603E A Perfect Problem 題解
- 理解位運算
- SQL位運算SQL