题解:P12555 [UOI 2024] AND Array
a_foolish_OIer · · 题解
记
把
于是对序列分块,块长为
但是后半部分明显内存访问连续常数小,块长开到
#include <bits/stdc++.h>
using namespace std;
mt19937 Rand(665431);
#define LL long long
#define i128 __int128
#define endl '\n'
const int N = 1e5 + 10;
const int M = 1e3 + 10;
const int inf = 1e9;
const int mod = 1e17 + 3;
int n,m,mxb,V,B;
int a[N],id[N],bl[N],br[N];
LL suf[N];
bitset <(1 << 20) + 10> vis[M];
map <int,int> res[M];
void solve(){
cin >> n >> mxb; B = 1700; V = (1 << mxb) - 1;
for (int i = 1 ; i <= n ; i++){
cin >> a[i];
id[i] = (i - 1) / B + 1;
if (!bl[id[i]]) bl[id[i]] = i;
br[id[i]] = i; m = id[i];
}
for (int i = 1 ; i <= m ; i++){
for (int j = bl[i] ; j <= br[i] ; j++)
if (a[j]) vis[i][a[j]] = 1;
for (int j = 0 ; j <= V ; j++)
for (int p = 0 ; p < mxb ; p++)
if (((j >> p) & 1) && vis[i][j ^ (1 << p)]) vis[i][j] = 1;
}
for (int i = n ; i >= 1 ; i--) suf[i] = suf[i + 1] + (!a[i] ? mxb * i : 0);
for (int i = 1 ; i <= n ; i++){
LL ans = suf[i];
for (int j = 0 ; j < mxb ; j++){
int num = (1 << j);
for (int p = i ; id[p] == id[i] ; p++)
if (a[p] && !(num & a[p])) num |= a[p],ans += p;
if (res[id[i]].count(num)){
ans += res[id[i]][num];
continue;
}
int z = num,ans2 = 0;
for (int k = id[i] + 1 ; k <= m && num != V ; k++)
if (vis[k][V ^ num])
for (int p = bl[k] ; p <= br[k] ; p++)
if (a[p] && !(num & a[p])) num |= a[p],ans2 += p;
res[id[i]][z] = ans2; ans += ans2;
}
cout << ans << ' ';
}
cout << endl;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int T = 1; //cin >> T;
while (T--) solve();
return 0;
}