D. Reverse Madness
题意
我们将一个字符串 s s s,分为连续的 k k k个部分,其中每一个部分的边界是 [ l [ i ] , r [ i ] ] [l[i], r[i]] [l[i],r[i]],现在给出q个查询,每个查询给出一个x,要求找到唯一一个满足 l [ i ] ≤ x ≤ r [ i ] l[i] \le x \le r[i] l[i]≤x≤r[i]的区间,并翻转 [ m i n ( x , l i + r i , − x ) , m a x ( x , l i + r i − x ) ] [min(x, l_i + r_i, - x), max(x, l_i + r_i - x)] [min(x,li+ri,−x),max(x,li+ri−x)]。
分析
由于k个区间是不相交的,所以显然x只属于一个区间,所以可以通过二分来找到x所在的区间,其次可以发现,对于每一个翻转操作,都是以区间中点为对称轴,x为一个边界做翻转,即每一个位置如果要翻转,那么就会课它对称的位置做交换,并且不会影响到其他
k
−
1
k-1
k−1个区间。
所以,如果一个位置被翻转偶数次,就相当于没有翻转。
题解
利用差分算法,每次对一个翻转的区间做标记,之后对字符串分成的
k
k
k个部分分别讨论,如果某个位置被翻转奇数次,那么就将这个位置翻转。
时间复杂度
O
(
q
l
o
g
n
)
O(qlogn)
O(qlogn)
代码
void solve()
{
int n, k;
cin >> n >> k;
string s;
cin >> s;
s = ' ' + s;
vector<int> l(k), r(k);
for (int i = 0; i < k; i++) cin >> l[i];
for (int i = 0; i < k; i++) cin >> r[i];
int q;
cin >> q;
vector<int> f(n+5, 0);
while (q --)
{
int x;
cin >> x;
auto xx = upper_bound(l.begin(), l.end(), x);//指导大于x的第一个位置
xx--;//--之后就找到了符合条件的区间
int t = xx - l.begin();
int a = min(x, l[t] + r[t] - x), b = max(x, l[t] + r[t] - x);
f[a] += 1;//差分
f[b+1] -= 1;
}
for (int i = 1; i <= n; i++) f[i] += f[i-1];
for (int i = 0; i < k; i++)//对每一个部分单独讨论
{
for (int j = l[i]; j <= (r[i] + l[i]) / 2; j++)
if (f[j] % 2)
{
swap(s[j], s[r[i] - (j - l[i])]);//如果被翻转奇数次,就将这个位置和其关于区间中点的对称位置做交换
}
}
s.erase(s.begin());
cout << s << endl;
// cout << fixed << setprecision(1);
}
E. Iva & Pav
题意
给定一个长度为 n n n的数组,给出q个询问,每个询问给出 l , k l, k l,k要求找到最大的右边界 r r r使得区间 [ l , r ] [l, r] [l,r]的按位与结果大于等于 k k k
分析
首先,对于按位与运算来说,只有两个数的一位二进制位都是1才会是1,所以其产生的区间结果一定是单调不增的,即最后最后得到的
r
r
r满足条件则
r
+
1
r+1
r+1一定不满足条件。
其次对于一个区间
[
a
,
b
]
[a, b]
[a,b],考虑一个数的二进制第
x
x
x位,只有区间内所有的数在第
x
x
x位有
b
−
a
+
1
b - a + 1
b−a+1个
1
1
1 即所有数在第x位都有1,最后得到的按位与结果第
x
x
x位才是1。
题解
由于存在单调性,可以考虑二分算法,二分找到 r r r的位置。其二分的check函数可以利用前缀和算法,预处理出区间内,每一个二进制位总共含有的1的数量,如果该二进制位在该区间内共含有 r − l + 1 r - l + 1 r−l+1个1,则 a n s + = ( 1 < < i ) ans += (1 << i) ans+=(1<<i),最后判断 a n s ≤ k ans \le k ans≤k 即可
代码
bool check(vector<vector<int>> &s,int k, int l, int r)
{
int sum = 0;
for (int i = 0; i < 31; i++)
{
if (s[r][i] - s[l-1][i] == r - l + 1) sum += (1 << i);
}
return sum >= k;
}
void solve()
{
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
vector<vector<int>> s(n + 1, vector<int>(35, 0));//前缀和数组 记录区间每一个二进制位总共含有1的数量
for (int i = 1; i <= n; i++)
for (int j = 0; j < 31; j++)
{
s[i][j] = s[i-1][j] + (a[i] >> j & 1);
}
int q;
cin >> q;
while (q --)
{
int a, k;
cin >> a >> k;
int l = a, r = n;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (check(s, k, a, mid)) l = mid;
else r = mid - 1;
}
if (check(s, k, a, r)) cout << r << ' ';
else cout << -1 << ' ';
}
cout << endl;
}