比賽連結
A.四捨五入
雖然讓找 \(i\),但列舉 \(i\) 很沒前途啊,所以考慮找到所有 \(j\) 的個數
發現對於一組合法的 \(i、j\) 需要滿足 \(i\in [kj,\ kj+0.5j)\ kj<=n\)
那麼我們對於每一個 \(j\),找到所有的 \(k\) 使得 \(kj<=n\),查分維護區間 \([kj,\ kj+0.5j)\)
時間複雜度為調和級數級別的
B.填算符
題解單飛咯!
題解連結
下發題解說的神馬東西,賽時根本想不到
講一個賽時想得到的 \(O(n\log 值域)\) 的思路,很好理解
我們處理出二進位制下每一位上的 1 的最後一次出現的位置,將第 \(i\ (i\in[0,60])\) 位上的 1 最後一次出現的位置記作 \(pos_i\)
同時我們設 \(H=n-k-1\) 為總共有的 bitor
的運算元
有以下結論:由於 \(pos_i\) 是 \(i\) 位上最後一個 1,所以一旦它後面放了一個 與,這一位上就是 0 了;若我們想要這一位為 1,必須至少滿足從 \(pos_i\) 到最後的運算子全是 bitor
。
發現有以下情況:
-
若 \(n-pos_i>H\),即 \(pos_i\) 之後需要放的運算子的數量比
bitor
的總運算元多,也就是說在 \(pos_i\) 之後我一定需要放bitand
操作,所以這種情況下這一位一定不對答案有貢獻 -
若 \(n-pos_i<H\),也就是說我可以從 \(pos_i\) 的前一個位置開始到最後全放
bitor
操作,那麼這樣第 \(i\) 位上可以是 1,為了使值最大,所以第 \(i\) 位上一定要是 1,所以從第 \(pos_i\) 位到最後必須全是bitor
操作,對於這種情況的 \(i\) 我們記為合法位 -
若 \(n-pos_i=H\),也就是說從第 \(pos_i\) 到最後的運算子可以全是
bitor
操作,但 \(pos_i\) 的前一位只能是bitand
所以我們特判從第 1 個位置到 \(pos_i\) 的前一位全放bitand
能不能讓到第 \(pos_i\) 個數時得到的值第 $\forall $ \(j 滿足 [pos_j=pos_i]\) 位為 1,若能則該位也為合法位,否則不合法
對於所有合法位的 \(pos\) 取最小值設為 \(end\),因為已經保證 \(end\) 到最後的預算符全是 bitor
,此時有一下兩種可能,而我們想盡量構成第二種可能:
-
\(end\) 的前一位預算符也為
bitor
,這樣我們一定能達到答案最大了,想使答案最優直接讓從 \(end-2\) 開始的 \(k\) 個運算子為bitor
就好了 -
\(end\) 的前一位在某些情況為
bitand
也是可以使答案最大的,所以我們判斷能不能讓 \(end\) 的前一位為bitand
同樣使答案最大;
發現可以的條件相當於從第 \(end-1\) 個數到最前面用僅剩的bitor
操作得到一個答案,使得這個答案第 $\forall $ \(i 滿足 [pos_i=end]\) 位為 1,若能滿足條件則第 \(end-1\) 個運算子為bitand
。
滿足條件的判斷又和上述的第三個情況判斷一致了,相當於以 \(end-1\) 為下界,再做一次求 \(min(合法的\ pos)\),實質上是不斷的遞迴。
形式化如下:
所以一個遞迴 \(dfs(end, H)\) 表示下界為 \(end\),還剩 \(H\) 個 bitor
操作,判斷能不能得到我想要的答案:
若不能則直接從第 \(end-2\) 開始的 \(k-res\) 個運算子全為 bitand
就是答案(\(res\) 為在之前的遞迴中已經確定的 bitand
的個數)
若能則第 \(end-1\) 個位置可以為 bitand
,並設 \(end'=min(這一層中合法的\ pos)\),繼續遞迴 \(dfs(end',H-(end-end'))\) 判斷第 \(end'-1\) 個位置能不能為 bitand
。
code:
#include<bits/stdc++.h>
#define Aqrfre(x, y) freopen(#x ".in", "r", stdin),freopen(#y ".out", "w", stdout)
#define mp make_pair
#define Type ll
#define qr(x) x=read()
typedef __int128 INT;
typedef long long ll;
using namespace std;
inline ll read(){
char c=getchar(); ll x=0, f=1;
while(!isdigit(c)) (c=='-'?f=-1:f=1), c=getchar();
while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48), c=getchar();
return x*f;
}
const int N = 1e6 + 5;
const int maxn = 1e8;
int n, k, K; ll a[N], b[N];
int la[62], pre[62][N], zh[62], X;
vector<int>v[N], ans, tem, num;
inline bool check(int pos, int op){ // 判斷從第一個運算子到第 pos 個全為 & 能不能使得到的值滿足條件
int now = pos + 1; ll x = 0;
for(int i : v[now]) x += (1 << i);
if(~X) x += (1 << X);
int y = a[1];
for(int i=2; i<=now; i++)
y = y & a[i];
if(y & x == x) return true;
return false;
}
inline void dfs(int pos, int H){ // 遞迴函式
if(pos <= 0 and H <= 0) return;
int now = pos + 1, end = 2e9;
bool f = true; X = -1;
for(int x : tem) v[pre[x][now]].clear(); //為方便更新新的一層的 V ,先清空
for(int x : tem){
if(pos - pre[x][now] > H or !pre[x][now]){
f = false; break;
}
else if(pos - pre[x][now] < H) // 合法則更新 end 並加入 V
end = min(end, pre[x][now] - 1), v[pre[x][now]].emplace_back(x);
else{
X = x;
if(pre[x][now] == 1 or check(pre[x][now] - 1, 1))
end = min(end, pre[x][now] - 1), v[pre[x][now]].emplace_back(x);
else f = false;
}
}
if(f) ans.emplace_back(pos), k--; // pos 位可以為 &,加到答案中
if(!k) return;
if(f and end >= k){
tem.clear(); for(int x : v[end+1]) tem.emplace_back(x);
dfs(end, H-(pos-end-1)); //繼續遞迴判斷 end 位可否為 &
}
else{
int cnt = k; // pos 位不可以為 &,則最優方案為從 pos-1 到 pos-cnt 全為 &
for(int i=pos-cnt; i<pos; i++)
k--, cout<<i<<" ";
return;
}
}
signed main(){ // bitop
Aqrfre(bitop, bitop);
qr(n); qr(k); K = k;
for(int i=1; i<=n; i++){
qr(a[i]);
for(int j=0; j<62; j++){
if(a[i] & (1ll << j)) pre[j][i] = la[j], la[j] = i;
else pre[j][i] = pre[j-ans.size()][i-1]; // 二進位制下第 j 位為 1 在第 i 個數之前一次出現的位置
}
}
if(k == n - 1){
for(int i=1; i<=k; i++) cout<<i<<" ";
return 0;
}
for(int j=0; j<62; j++) // V 存當前這一層遞迴的下界包含的 最後一個 1 出現在這個下界的 二進位制位
if(la[j]) zh[j] = la[j], v[zh[j]].emplace_back(j);
int H = n - 1 - k, endi = 1e9; bool go = false;
for(int i=0; i<62; i++){ // 把第一次遞迴剖出來單獨做
if(!zh[i]) continue;
if(n - zh[i] > H) continue;
if(n - zh[i] < H){
endi = min(endi, zh[i] - 1);
continue;
}
if(go) continue;
if(n - zh[i] == H){
if(check(zh[i] - 1, 0)){ //特殊的:合法直接輸出
for(int i=1; i<=k; i++)
cout<<i<<" ";
return 0;
}
go = true;
}
}
for(int x : v[endi+1]) tem.emplace_back(x); //tem 暫存下界這個數的 V
H -= (n - endi - 1);
dfs(endi, H);
sort(ans.begin(), ans.end());
for(int x : ans) cout<<x<<" ";
return 0;
}