1. 程式人生 > >【bzoj5108】[CodePlus2017]可做題 拆位+亂搞

【bzoj5108】[CodePlus2017]可做題 拆位+亂搞

答案 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]可做題 拆位+亂搞