數論——異或,兩道水題。
第一題:(沒有鏈接)
題目大意:給你n個數(n <= 1000000),第i個數用ai表示(每個a <= 1000000),求出每個數與其之後的數的xor和。
舉個例子吧,比如三個數1 2 3答案就應該為1 xor 2 + 1 xor 3 + 2 xor 3 = 4;
題解:
首先有一個O(n*n)的算法,就是暴力枚舉,可以過40%數據。
程序大體:
for (int i = 1;i < n;i ++) for (int j = i + 1;j <= n;j ++) ans += a[i] ^ a[j];
很明顯TLE,考慮一下兩個數的二進制的某一位xor能產生貢獻的條件是什麽?就是兩位不同。。。 也就是其中一個是1,另一個是0二進制下的這一位就可以產生貢獻。咦?好像有很大的用。那麽我們可以很容易的想到
代碼如下:
#include <cstdio> #include <iostream> using namespace std;int n; long long ans, a; long long cnt[30]; int main(){ scanf("%d", &n); for (int i = 1;i <= n;i ++){ scanf("%lld", &a); for (int i = 1;i <= 20;i ++) if (1 << (i-1) & a) cnt[i]++; } for (int i = 1;i <= 20;i ++) ans += cnt[i] * (n-cnt[i]) * (1<< i-1); printf("%lld", ans); } //tips:別忘了用long long
第二題:題目鏈接
題目大意:給你n個數(n <= 1e5),每個數為ai(每一個a <= 1e9)求每個區間xor值的和,這裏吐槽一下,說的數據是1e9實際只有2的16次方的數據(洛谷第一個討論看見的)
舉個例子吧,比如兩個數1 2,你需要輸出的就是1 + 2 + 1 xor 2 = 6,單獨的一個數也叫一個區間。
題解:同樣的暴力枚舉每一區間復雜度O(n*n),TLE。。。做這道題需要知道若a xor b = c, 那麽a xor c = b;(我不會證明,這是xor的運算性質),然後我們同樣的向二進制方向想,對於每一位產生貢獻進行考慮,若[l, r]這個區間對於這一位能產生貢獻的條件就是其中1的個數為奇數且0的個數為偶數,或者0的個數為奇數且1的個數為偶數。那麽我們可以求出xor的前綴也就是sum[i]表示前i個數的異或值。那麽統計答案只需要求出對於二進制下的每一位這n個前綴中有幾個1,用cnt表示,有幾個0(也就是n - cnt)然後讓每一個1與每一個0組合形成一個區間然後統計就是cnt (n-cnt) 1 << (k -1);
但是這樣是錯的,你會發現你讓他們組合了卻沒有考慮到1-X這樣的區間也就是讓還有cnt個也可以產生貢獻。所以答案統計應該是(cnt (n - cnt) + cnt) 1 << (k - 1)也就是cnt (n - cnt + 1) 1 <<(k -1);
代碼:
#include <cstdio> #include <iostream> const int maxn = 100010; using namespace std; int n; long long ans; long long sum[maxn]; int main(){ scanf("%d", &n); for (int i = 1;i <= n;i ++) scanf("%lld", &sum[i]), sum[i] = sum[i-1] ^ sum[i]; for (int i = 1;i <= 17;i ++){ long long cnt = 0; for (int j = 1;j <= n;j ++) if (sum[j] & (1 << (i-1))) cnt ++; ans += cnt * (n - cnt + 1) * (1 << i - 1); } printf("%lld", ans); return 0; } //tips:別忘了開long long
蒟蒻開始跑路。。。
數論——異或,兩道水題。