牢了很久才看懂的一道题

#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
using namespace std;
const int mod=998244353;
typedef pair<int,int>pii;
const int N=3e5;
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
int num[N],inv[N];//阶乘以及阶乘的逆元
//ans=num[a]*inv[a-b]%mod*inv[b]%mod;
int kmi(int a,int b){
    int res=1;
    while(b){
        if(b&1)res=res*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return res;
}

void init(){
     num[0]=1,inv[0]=1;
    for(int i=1;i<=2e5;i++){//预处理出2e5内的阶乘以及其逆元
        num[i]=num[i-1]*i%mod;
        inv[i]=kmi(num[i],mod-2);
    }
}

/*
开始最不理解的一个点是为什么要从j(第一个不符合的位置)开始转移而不是j+1开始
反例就是对于一个i,假设我往前找一位(i-1位置)直接遇到&不等于0了
i这个位置加到这相当于对答案没影响 例如 1 2 3中的3,相当于dp[3]=dp[2],而不是dp[3]=0
所以任何一个位置最坏也能从前一个位置转移过来(直接在i前放加号)
所以j就是找的最远能放加号的位置前面的那个数
*/
void solve(){
    int n;cin>>n;
    vector<int>a(n+1);
    vector<int>pre(n+1);
    int lst=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        pre[i]=lst;
        if(a[i]) lst=i;
    }
    vector<int>dp(n+1),s(n+1);//
    s[0]=dp[0]=1;//考虑前0个数有1个方案数
    for(int i=1;i<=n;i++){
        int j=i;
        int val=0;
        while(j&&((val&a[j])==0)){//本质上在看最后一个加号最远能放到多前,然后前面不受第i个元素影响,既定怎么放就怎么放
            val|=a[j];
            j=pre[j];
        }
        //j现在是第一个并不进来的数或者0,加号最多能放在他后面,也就是最多能从他转移过来
        if(j)dp[i]=(s[i-1]-s[j-1]+mod)%mod;//j到i-1的后面都能放加号
        else dp[i]=s[i-1]-0;//0到i-1后面都能放加号
        s[i]=(s[i-1]+dp[i])%mod;
    }
    cout<<dp[n];
}
signed main(){
    ios_base::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
    int T=1;cin>>T;
    while(T--){
        solve();
        cout<<endl;
    }
    return 0;
}