【bzoj5108】[CodePlus2017]可做題 拆位+亂搞
阿新 • • 發佈:2017-12-08
答案 names cstring for per 直接 color min 異或
輸出一個整數表示可能的最小值
題目描述
給出一個長度為 $m$ 的序列 $a$ ,編號為 $a_1\sim a_m$,其中 $n$ 個位置的數已經確定,剩下的位置的數可以任意指定。現在令 $b$ 表示 $a$ 的前綴異或和,求 $\sum\limits_{i=1}^mb_i$ 的最小值。
輸入
輸入第一行兩個非負整數n,m,分別表示原始序列a的長度及剩余元素的個數。 之後m行,每行2個數i,ai,表示一個剩余元素的位置和數值。 1<=N<=10^9,0<=M<=Min(n,10^5),0<=ai<=10^9 註意未知的 ai 可以超過已知 ai 的範圍。 保證輸入中所有的 i 不同,且滿足 1 ≤ i ≤ n。輸出
樣例輸入
5 3
4 0
3 7
5 0
樣例輸出
7
題解
拆位+亂搞
首先容易發現:每一個連續段的影響是獨立的。
進一步可以發現:對於兩個連續段之間沒有填數的一段,該未填段除最後一個數以外的數的異或和均在取0(顯然可以取到)時最優,而該未填段最後一個數只對自己以及後面的連續段產生影響。
更加具體地,若該未填段的最後 $b_i$ 是 $x$ ,後面連續段的數的前綴異或和為 $c_1\sim c_l$ ,則代價就是 $x+\sum\limits_{i=1}^lx\ xor\ c_i$ 。
顯然每一位互不影響,於是我們可以拆位,統計出前綴異或和中該位0和1的個數,進而判斷 $x$ 的這一位取0和取1時哪一個更優,然後計算答案即可。
這裏需要註意一個坑點:如果第一個連續段是從第一個位置開始的,由於沒有前一個位置,不能“欽定”最優解,需要特判這種情況,直接計算。
時間復雜度 $O(n\log n)$
#include <cstdio> #include <cstring> #include <algorithm> #define N 100010 using namespace std; struct data { int p , v; bool operator<(const data &a)const {return p < a.p;} }a[N]; int s[N] , cnt[31] , tot; int main() { int m , i , j , c; long long ans = 0; scanf("%*d%d" , &m); for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &a[i].p , &a[i].v); sort(a + 1 , a + m + 1); for(c = 1 ; c <= m ; c ++ ) { tot = 1 , s[1] = a[c].v; while(c < m && a[c + 1].p - a[c].p == 1) tot ++ , s[tot] = a[++c].v ^ s[tot - 1]; if(!(c - tot) && a[1].p == 1) for(i = 1 ; i <= tot ; i ++ ) ans += s[i]; else { for(i = 0 ; i < 30 ; i ++ ) { cnt[i] = 0; for(j = 1 ; j <= tot ; j ++ ) if(s[j] & (1 << i)) cnt[i] ++ ; ans += (1ll << i) * min(cnt[i] , tot - cnt[i] + 1); } } } printf("%lld\n" , ans); return 0; }
【bzoj5108】[CodePlus2017]可做題 拆位+亂搞