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";
}