题解:P11971 「ALFR Round 7」T4 xor xor
YangXiaopei · · 题解
题意
给你一段01串,每次询问你一段区间和一个
题解:
分几种情况。
-
在
[l, r] 这一段中1,0 的个数均大于等于k 。 -
在
[l, r] 这一段中1 的个数均小于k 。 -
在
[l, r] 这一段中0 的个数均小于k 。
先看第一种情况,在这种情况下,第一个子序列只选
那么此时答案就是
再看第二种情况。题目中保证
而我们发现,想要异或最大,就要尽量保证在同一位上第一个子序列和第二个子序列上的数不一样,那我们就要尽量保证能造成贡献的
而异或是对于每一位的,也就是说在某一位上互换不影响答案。
那我们不妨钦定把
而我们发现,只要第一位是
而
而且这个后缀开头越靠后答案必然更优。
所以我们可以二分查找这个再此之前只选
而一段区间的答案我们可以用类似前缀和维护。
至于
代码
::::info[丑陋至极的代码]{open}
#include<bits/stdc++.h>
#define mod 1000000007
#define int long long
#define maxn 1000005
using namespace std;
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-'){
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
void write(int x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9){
write(x / 10);
}
putchar(x % 10 + '0');
return;
}
int n, q, l, r, k;
int d[maxn], f[maxn], dd[maxn], ff[maxn], bs[maxn];//d,f,dd,ff,bs分别表示前缀1的个数,前缀0的个数,前缀二进制对应的数,取反后前缀二进制对应的数,以及二的幂次方
string s;
int ksm(int a, int b){//快速幂
int s = 1;
while(b){
if(b & 1){
s = s * a % mod;
}
a = a * a % mod, b >>= 1;
}
return s;
}
int get(int x){//能把k个选满的答案
int ans = ksm(2, x) - 1;
ans = (ans % mod + mod) % mod;
return ans;
}
int do_d(int l, int r){//1为正值l-r这一段对应的答案
int ans = (dd[r] - dd[l - 1] * bs[r - l + 1]) % mod;
return ans;
}
int do_f(int l, int r){//0为正值l-r这一段对应的答案
int ans = (ff[r] - ff[l - 1] * bs[r - l + 1]) % mod;
return ans;
}
int solve(int x, int l, int r, int op){//表示先选x个1/0,再选l-r这一段
if(op == 1){
int ans = ((ksm(2, x) - 1) * ksm(2, r - l + 1) + do_d(l, r)) % mod;
ans = (ans % mod + mod) % mod;
return ans;
}
else{
int ans = ((ksm(2, x) - 1) * ksm(2, r - l + 1) + do_f(l, r)) % mod;
ans = (ans % mod + mod) % mod;
return ans;
}
}
void work(){
l = read(), r = read(), k = read();
int x = d[r] - d[l - 1], y = f[r] - f[l - 1];
if(x >= k && y >= k){//1,0都能选满k个
write(get(k)), puts("");
return;
}
else if(x < k){//1选不满
int ll = l, rr = r, ans = ll;
while(ll <= rr){//二分寻找再此之前只选1,在此之后选后缀的断点
int mid = (ll + rr) >> 1;
if(r - mid + 1 + d[mid - 1] - d[l - 1] >= k){
ans = mid;
ll = mid + 1;
}
else{
rr = mid - 1;
}
}
write(solve(d[ans - 1] - d[l - 1], ans, r, 1)), puts("");
return;
}
else{//0选不满
int ll = l, rr = r, ans = ll;
while(ll <= rr){//同上
int mid = (ll + rr) >> 1;
if(r - mid + 1 + f[mid - 1] - f[l - 1] >= k){
ans = mid;
ll = mid + 1;
}
else{
rr = mid - 1;
}
}
write(solve(f[ans - 1] - f[l - 1], ans, r, 0)), puts("");
return;
}
return;
}
signed main(){
n = read(), q = read();
cin >> s;
s = ' ' + s, bs[0] = 1;
for(int i = 1; i <= n; i++){
d[i] = d[i - 1] + (s[i] - '0'), f[i] = f[i - 1] + !(s[i] - '0');//d,f分别表示1,0的个数
dd[i] = (dd[i - 1] * 2 + (s[i] - '0')) % mod, ff[i] = (ff[i - 1] * 2 + !(s[i] - '0')) % mod, bs[i] = bs[i - 1] * 2 % mod;//求一段对应的值
}
while(q--){
work();
}
return 0;
}
::::