[省選聯考 2024] 魔法手杖

spdarkle發表於2024-10-09

一年之後再看好歹是會雙log做法的84分的,雖然可能被卡常

首先顯然有 \(x\oplus y\le x+y\)

對於一個最優的方案 \(S,x\) 你顯然如果不影響 $\oplus $ 部分的最值的話移走的最優的。

所以我們只會將會影響 $\oplus $ 部分最值的留在 \(S\)

考慮二分答案 \(mid\),判斷有沒有 \(\ge mid\) 的方案。

則因為 \(x\oplus y\le x+y\),所以事實上有:

\[mid\le \min(\min_{i\in S}(a_i+x),\min_{i\notin S}(a_i\oplus x))\implies mid\le \min(\min (a_i+x),\min_{i\notin S}(a_i\oplus x)) \]

而那麼在滿足 \(x\ge mid-a_{\min}\) 的條件之下,如果不滿足 \(a_i\oplus x\ge mid\),則必然需要加入 \(S\),且儘可能少加入。

則可以得到存在合法方案的充要條件是:

\[\exists x\in [mid-a_{\min},2^k-1],\sum_{a_i\oplus x<mid}b_i\le m \]

考慮將 \(a_i\oplus x\)\(mid\) 作比較,感覺 \(x\) 很容易貪心地填 \(1\)

轉化物件,以 \(a_i\) 為主元。

感覺如果有 \(a_i\oplus x<mid\),那麼顯然可以數位DP劃分出 $\log $ 個區間,那麼就可以變成區間加,然後全域性最小值查詢了。

這樣就得到了 $3\log $ 做法

最佳化?

其實還是對應了 \(trie\) 上的點,所以可以變成 \(trie\) 上的單點加全域性 \(\min\) 維護。

感覺還是容易的,得到了 $2\log $ 做法。

然後我們需要一個字尾 min,可以實現

考慮Trie的實現細節

其實是看 \(mid\) 的每一位和 \(x\) 當前的每一位,然後統一算所有走到這裡的 \(a\) 的貢獻,其實這個 \(a\) 應該是在子樹內部。

所以我們需要維護一個子樹內 \(b\) 的和。

  • \(bit(mid)=1\),則

    \(x=1\) 相當於繼續遞迴左子樹,而左子樹可以直接加上右子樹的 \(b\)

    \(x=0\) 相當於繼續遞迴右子樹,而右子樹可以直接加上左子樹的 \(b\) 了。

  • \(bit(mid)=0\),則

    \(x=1\) 相當於繼續遞迴右子樹,左子樹已經解決

    \(x=0\) 相當於繼續遞迴左子樹,右子樹已經解決

所以可以子樹內維護當前位置 \(mid\)\(1\),更低位置全 \(0\) 在子樹內是否有解,也就是要求 \(x\ge mid-a_1\),所以我們維護有解的最大 \(x\)

我們不妨設一個 \(sol(p,mid,dep,now,mna,sumb)\)

也就是當前劃分到的集合 \(S\) 裡的 \(mna\),當前節點是 \(p\),深度 \(dep\),當前得到的答案是 \(mid\),當前填的 \(x\)\(now\),當前已經得到了 \(sumb\) 的加法標。

列舉當前 \(mid\)\(now\) 怎麼取的 \(4\) 類情況討論即可

ll sol(int p,int dep,ll mid,ll now,ll mna,int sumb){
    if(sumb>m)return 0;
    if(dep<0)return mid;
    ll res=0;
    //mid=1,now=1,遞迴左子樹,算上右子樹b
    //mid+=pw[d],now+=pw[d]
    int lc=ch[p][0],rc=ch[p][1];
    ll lmna=min(ma[rc],mna),rmna=min(ma[lc],mna);
    if(pw[dep]+now+pw[dep]-1+lmna>=mid+pw[dep]){
        res=max(res,sol(lc,dep-1,mid+pw[dep],now+pw[dep],lmna,sumb+sb[rc]));
    }
    //mid=1,now=0,遞迴右子樹,算上左子樹b
    if(now+pw[dep]-1+rmna>=mid+pw[dep]){
        res=max(res,sol(rc,dep-1,mid+pw[dep],now,rmna,sumb+sb[lc]));
    }
    if(res>0)return res;
    //mid=0,now=1,遞迴右子樹,左子樹無貢獻,mna無需更新
    if(pw[dep]-1+pw[dep]+now+mna>=mid){
        res=max(res,sol(rc,dep-1,mid,now+pw[dep],mna,sumb));
    }
    if(pw[dep]-1+now+mna>=mid){
        res=max(res,sol(lc,dep-1,mid,now,mna,sumb));
    }
    return res;
}

然後優先 \(mid\)\(1\) 的解,大力遞迴就好了。我們每一層根據 \(mna+now\ge mid\) 來減枝,可以保證每次往下搜的時候只要能夠遞迴就可以得到解,所以每個點只會遞迴一次,複雜度 \(O(Tnk)\)

這貪心是真NB

這個題涉及到了很多技巧:

  • 貪心將不會影響答案的東西移走,使最優方案更容易滿足邊界條件
  • 整體與單體的視角轉化\(\sum_{a_i\oplus x<mid}b_i\) 從列舉 \(x\) 單體——整體計算變成對於每個 \(a_i\)單體整體統計貢獻,有時候正難則反。
  • 限制條件的解決形式,如 \(x\ge [mid-a_{\min},2^k-1]\) 不用再計算單 \(a_i\) 貢獻時考慮邊界,而是在貢獻完之後整體照邊界
  • 位運算題目二分答案往往可以被按位貪心討論簡化掉