专栏文章
题解:CF2038D Divide OR Conquer
CF2038D题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @mimz92id
- 此快照首次捕获于
- 2025/12/01 17:59 3 个月前
- 此快照最后确认于
- 2025/12/01 17:59 3 个月前
CF2038D Divide OR Conquer
一个比较好理解,但实现没有那么简洁的做法。
solution
可以用 st 表维护区间或值,复杂度 。
设 表示当前考虑了前 位,最后一段的异或和为 。转移是容易的。
然而这样光状态数就是 的,先考虑减少状态。
显然,最后一段一定包含 。对于一段固定 , 不断减小的后缀异或和,已经变为 的位置不可能再变为 。也就是说,对于每个 , 只有 种合法取值。所以总的合法状态数就是 的。
接着优化转移。对于一个 ,考虑哪些 可以转移到它。根据上文分析,后缀异或值随着 的减小变大的位置个数是 的,于是对于每一种合法异或值 ,找出 的范围 。这个位置可以二分找出。(具体来说,每次找到最远的 使得 不变,那么 就是一个“断点”。)
那么当 满足 (因为 是被分进最后一段,所以应该从 转移。)时,有 。这个东西一看就是主席树可以维护的形式,所以上主席树。……?
别急。注意到 dp 顺序显然是按 从小到大,所以可以把查询挂在查询端点上离线处理。这时候只需要对于 这一维单点加,查询前缀和,树状数组即可。
复杂度 。
code
写的有点混乱邪恶。
CPP#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define mkp make_pair
#define fi first
#define se second
#define eb emplace_back
const int maxn=200004,maxl=32,mxl=20,Maxn=maxn*31,mod=998244353;
void adt(int &x,int y){x+=y,(x>=mod)&&(x-=mod);}
int ad(int x,int y){return x+=y,(x>=mod)?(x-mod):x;}
int n;
int a[maxn];
int pw[maxl],lg[maxn],o[maxn][maxl];
void oinit(){
pw[0]=1;for(int i=1;i<=mxl;i++) pw[i]=pw[i-1]<<1;
for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++) o[i][0]=a[i];
for(int j=1;j<=mxl;j++) for(int i=1;i+pw[j]-1<=n;i++)
o[i][j]=o[i][j-1]|o[i+pw[j-1]][j-1];
}
inline int qry(int L,int R){//O(1) 查询区间或值
int k=lg[R-L+1];
return o[L][k]|o[R-pw[k]+1][k];
}
int b[Maxn],len;
int liz[maxn],val[maxn][maxl];
vector <pii> vec[maxn];
void sol(int Pos){//找断点
int pos=Pos,v=a[Pos];
vec[Pos-1].eb(mkp(Pos,1));
while(pos){
b[++len]=v,val[Pos][++liz[Pos]]=v;
int l=1,r=pos,md;
while(l<r){md=(l+r>>1);(qry(md,Pos)==v)?(r=md):(l=md+1);}
pos=l;if(pos==1) break;
v|=a[--pos];
vec[pos-1].eb(mkp(Pos,-liz[Pos]));
vec[pos-1].eb(mkp(Pos,liz[Pos]+1));
//将查询挂在左右端点上
}
}
int dp[maxn][maxl];
namespace BIT{
int tr[Maxn];
inline void Add(int i,int x){for(;i<=len;i+=i&-i) adt(tr[i],x);}
inline int Qry(int i){int res=0;for(;i;i&=i-1) adt(res,tr[i]);return res;}
}using namespace BIT;
int ans;
void DP(){
b[++len]=0;sort(b+1,b+1+len);len=unique(b+1,b+1+len)-(b+1);
val[0][++liz[0]]=0,dp[0][1]=1;
for(int i=0;i<=n;i++) for(int j=1;j<=liz[i];j++) val[i][j]=lower_bound(b+1,b+1+len,val[i][j])-b;
for(int i=0;i<n;i++){
for(int j=1;j<=liz[i];j++) Add(val[i][j],dp[i][j]);
for(pii o:vec[i]){
if(o.se<0){o.se=-o.se;adt(dp[o.fi][o.se],mod-Qry(val[o.fi][o.se]));}
else{adt(dp[o.fi][o.se],Qry(val[o.fi][o.se]));}
}
}
for(int j=1;j<=liz[n];j++) adt(ans,dp[n][j]);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
oinit();
for(int i=1;i<=n;i++) sol(i);
DP();
cout<<ans<<'\n';
return 0;
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...