21.7.7 t1
阿新 • • 發佈:2021-07-07
tag:貪心,二進位制
顯然要按位貪心。
對於當前的答案字首,掃一遍求出每個數至少要右移幾次才能貼合當前答案。
對於一個合併樹來說,本來是or
在一起然後右移 \(1\),可以看作是每個數先右移若干(合併樹上的深度)次,然後再全部or
在一起。
然後可以發現 \(2\) 個需要右移 \(x\) 次的點可以合在一起變成一個需要右移 \(x-1\) 次的點。
於是可以 \(O(w)\) 判斷是否合法。(只要最終能夠合成一個右移 \(0\) 次的點,就合法,其他的點可以直接用刪除操作刪掉)
然後就得到了一個 \(O(Tnw^2)\) 的做法,鬆一鬆可以過。
當然我們不能滿足於 \(O(\)鬆\()\)
其實是我卡不過去
可以用一個 \(Move\) 陣列(long long
)去記錄對於每一個數,每一種右移是否合法。二進位制第 \(i\) 位為 \(1\) 就表示右移 \(i\) 位合法。
初值設為 \(2^{w+1}-1\),然後每次如果確定了答案當前這一位 \(j\) 為 \(0\),就要修改一下 \(Move\)。
Move[i] &= ~a[i]>>j
模擬一下就知道是什麼原理了。
然後就省去了列舉的步驟,直接取lowbit。
#include<bits/stdc++.h> using namespace std; template<typename T> inline void Read(T &n){ char ch; bool flag=false; while(!isdigit(ch=getchar()))if(ch=='-')flag=true; for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48)); if(flag)n=-n; } typedef long long ll; enum{ MAXN = 100005 }; int n, w; ll a[MAXN], ans, Move[MAXN], tmp[MAXN]; inline char check(){ static ll cnt[61]; for(int i=0; i<=w; i++) cnt[i] = 0; for(int i=1; i<=n; i++) cnt[__builtin_ffs(Move[i])-1]++; /* __builtin_ffs(x) 最小的1是第幾位 */ for(ll i=0, req=1; i<=w; i++){ if(req <= cnt[i]) return true; req = (req-cnt[i])<<1; } return false; } int main(){ int T; Read(T); while(T--){ Read(n); Read(w); for(int i=1; i<=n; i++) Read(a[i]), Move[i] = (1ll<<w+1)-1; ans = 0; for(int i=w-1; ~i; i--){ copy(Move+1,Move+n+1,tmp+1); for(int j=1; j<=n; j++) Move[j] &= ~a[j]>>i; if(!check()) ans |= 1ll<<i, copy(tmp+1,tmp+n+1,Move+1); } cout<<ans<<'\n'; } return 0; }