二进制十进制
我们得到了一个长度为n 的二进制字符串 S,其中每个字符为'1'或'0'。< / p>
我们被要求对字符串执行几个查询。
在每个查询中,我们都得到整数 L 和 R 。 并且我们必须以十进制表示形式来告诉子字符串 S [l..r] 的值。
示例测试用例:
Input:
1011 (string S)
5 (number of queries)
1 1 (l, r)
2 2
1 2
2 4
1 4
Output:
1 (1 * 2^0 == 1)
0
2
3 (0 * 2^2 + 1 * 2^1 + 1 * 2^0)
11 (1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 1 * 2^0 = 11)
约束
1 < N < 10^5
1 < Q < 10^5
由于数量可能非常大,我们需要以模数 10 ^ 9 + 7进行打印。
方法
因此,基本上,我们需要将二进制表示形式的子字符串S [l..r]转换为十进制。
我预先计算对所有 i:[0,n-1] S [i ... n-1] 的结果 阵列B 中的“ strong”。 因此,现在 B [i] 代表子字符串 S [i..n-1] 的十进制数字表示。
vector<int> pow(1e5, 1);
for(int i = 1; i < 1e5; i++) {
pow[i] = (pow[i - 1] * 2) % mod;
}
string s;
getline(cin, s);
vector<int> B(n, 0);
int prev = 0;
for(int i = 0; i < n; i++) {
B[(n - 1) - i] = (prev + (s[(n - 1) - i] == '1' ? pow[i] : 0)) % mod;
prev = B[(n - 1) - i];
}
while(q--) {
int l, r;
cin >> l >> r;
cout << ((B[l] - (r + 1 < n ? B[r + 1] : 0) + mod) % mod) / pow[n - (r + 1)]<< "\n";
}
return 0;
通过上述方法,仅通过了示例测试用例,其他所有情况都给出了错误答案(WA)。
我什至尝试使用段树来解决此问题,但这也不起作用。
解决此问题的正确方法是什么?
答案 0 :(得分:3)
将V[k]
定义为S
的第k
位数字。
然后是子字符串S[l..r] = (V[l] - V[r+1]) / 2^(n - r - 1)
的值。 (类似的事情,我可能会犯一个错误。请举一些小例子。)
现在,关于10^9 + 7
的有用事实是它是素数。 (前10个数字为质数。)这意味着除以2等于乘以2^(10^9 + 5)
。您可以通过重复平方求出该常数。使用重复平方可以非常有效地将常数提高到高倍。
以此,您可以为V
创建一个查找表,然后及时O(log(n))
进行查询。
答案 1 :(得分:0)
这似乎与常规求和范围查询相同,除了(1)我们需要存储部分和mod 10 ^ 9 + 7,(2)在检索期间,我们需要“移位”全部和的相关部分用右边部分的长度求和。在这种情况下,要“移动”将意味着乘以2 ^(length_of_suffix)mod 10 ^ 9 +7。当然,将段mod 10 ^ 9 + 7相加。
但是btilly的answer似乎要简单得多:)