题目大意
给一个二进制数 n n n,求满足 0 ≤ j ≤ i ≤ n 0 \leq j \leq i \leq n 0≤j≤i≤n且 ( i & n = i ) & & ( i & j = 0 ) (i \&n=i) ~~ \&\& ~~(i\&j=0) (i&n=i) && (i&j=0)的数对 ( i , j ) (i,j) (i,j)有多少对, n n n可能有前导 0 0 0。
解题思路
开始推了样例,首先肯定要去找 i i i,以第二个样例来看,若要满足 i & 1010 = i i\&1010=i i&1010=i,我们从左向右按位来看,则 i i i的第一位可能是 1 1 1或者 0 0 0,只有这样做完运算后这一位才不会变。然后是第二位,我们知道 i i i的这一位必须是 0 0 0,否则该位就发生了改变。然后得出结论。求 i i i时, S S S对应的每一位若为 1 1 1该位则有两种,若为 0 0 0则该位只有一种。因此可以求出所有的 i i i(还知道,这里求出的 i i i肯定都是小于等于 S S S的)。然后求 j j j,先忽略求出 i i i的前导 0 0 0,然后简单推导可以知道 j j j对应的规律和求 i i i时刚好相反,即若非前导 0 0 0的某一位为 0 0 0,则对应两种情况,若为 1 1 1则对应一种情况。貌似思路有了,可不会写,还是太菜~~
正解思路:从右往前看,每一位遇到 1 1 1就让他做 i i i的第一位。看除了第一位后面有多少个 0 0 0有多少个 1 1 1,设这个 1 1 1的低位中有 m m m个 1 1 1和 n n n个 0 0 0,推导得出有 2 n ∗ ∑ C m i 2 i 2^n* \sum C_m^i2^i 2n∗∑Cmi2i种方案。这个式子能不能化简呢?答案是能:
-
∑
C
n
i
∗
2
i
=
3
n
\sum C_n^i*2^i=3^n
∑Cni∗2i=3n
证明: ( x + 1 ) n = C n 0 ∗ x 0 + C n 1 ∗ x 1 + C n 2 ∗ x 2 + ⋅ ⋅ ⋅ + C n n − 1 ∗ x n − 1 + C n n ∗ x n (x+1)^n=C_n^0*x^0+C_n^1*x^1+C_n^2*x^2+···+C_n^{n-1}*x^{n-1}+C_n^n*x^n (x+1)n=Cn0∗x0+Cn1∗x1+Cn2∗x2+⋅⋅⋅+Cnn−1∗xn−1+Cnn∗xn
令 x = 2 x=2 x=2,则有 3 n = C n 0 ∗ 2 0 + C n 1 ∗ 2 1 + C n 2 ∗ 2 2 + ⋅ ⋅ ⋅ + C n n − 1 ∗ 2 n − 1 + C n n ∗ 2 n 3^n=C_n^0*2^0+C_n^1*2^1+C_n^2*2^2+···+C_n^{n-1}*2^{n-1}+C_n^n*2^n 3n=Cn0∗20+Cn1∗21+Cn2∗22+⋅⋅⋅+Cnn−1∗2n−1+Cnn∗2n
还要注意,第一位我们最后是没有考虑它为0的情况,第一位为0的话后面只能都是0,这样最后再加一即可
代码:
#include <iostream>
#include <string>
using namespace std;
typedef long long ll;
const int Mod=1e9+7;
const int maxn=1e5+10;
ll quick_mod(ll x,ll n){
ll ans=1;
while(n){
if(n&1) ans=ans*x%Mod;
n>>=1;
x=x*x%Mod;
}
return ans;
}
int main()
{
int T;
string str;
scanf("%d",&T);
while(T--){
string s="";
cin>>str;
int k=0;
while(str[k]=='0') ///处理初始串的前缀0
k++;
for(int i=k;i<str.size();i++)
s+=str[i];
//cout<<s<<endl;
int num1=0,num2=0;
ll ans=0;
for(int i=s.size()-1;i>=0;i--){
if(s[i]=='1'){
ans=(ans%Mod+(quick_mod(2,num1)*quick_mod(3,num2))%Mod)%Mod;
num2++;
}else num1++;
}
ans=(ans+1)%Mod; ///注意
printf("%lld\n",ans);
}
return 0;
}