目录

2025“钉耙编程”中国大学生算法设计春季联赛补题

2025"钉耙编程"中国大学生算法设计春季联赛补题

1001 数列计数

难点

给定两个长度为\(n\)的正整数序列\(a_i\)与\(l_i\),对于每个\(a_i\),求有多少个大小在\([0,l_i]\)内的\(b_i\)满足 \(a_i\&b_i=b_i\)

思路

对于\(a_i\)和\(l_i\)在二进制形式下的每一位,可以发现如下规律:

\(a_i\)二进制形式下的第\(j\)位 \(l_i\)二进制形式下的第\(j\)位 第\(j\)位对答案的贡献\(f(i,a_i,l_i)\)
0 0 0
1 0 0
0 1 2的\(a_i\)在\([0,j-1]\)二进制位下1的个数次幂
1 1 2的\(a_i\)在\([0,j-1]\)二进制位下1的个数次幂+\(f(i-1,a_i-2^j,l_i-2^j)\)

也就是可以递归来求解,有些细节需要特别处理一下,具体可以看代码

代码实现

c++ 复制代码
vector<int>suf(33);
int mod=998244353;
int f(int i,int x,int y){
    if(i==0){
        if(x==1&&y==1) return 2;
        else return 1;
    }
    if(y>>i&1){
        if(x>>i&1){
            return (1ll<<suf[i-1])+f(i-1,x-(1ll<<i),y-(1ll<<i));
        }
        return (1ll<<suf[i-1]);
    }    
    if(x>>i&1) return f(i-1,x-(1ll<<i),y);
    return f(i-1,x,y);
}
void solve(){
    int n;cin>>n;
    vector<int>a(n+1),l(n+1);
    rep(i,1,n) cin>>a[i];
    rep(i,1,n) cin>>l[i];
    int ans=1;
    rep(i,1,n){
        int tmp=0;
        if(a[i]<=l[i]){
            tmp=(1ll<<__builtin_popcount(a[i]));
        }
        else{
            suf[0]=a[i]&1;
            rep(j,1,30){
                suf[j]=suf[j-1]+(a[i]>>j&1);
            }            
            tmp=f(30,a[i],l[i]);
        }
        ans=(ans*tmp)%mod;
    }
    cout<<ans<<"\n";
}
本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除