一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情。
题目描述
这是codeforces的线段树专题。
B - Segment Tree, part 1 - K-th one
In this problem, you need to add to the segment tree the operation of finding the k-th one.
Input
The first line contains two numbers n and mm (1≤n,m≤1000001≤n,m≤100000), the size of the array and the number of operations. The next line contains n numbers ai, the initial state of the array (ai∈{0,1} ). The following lines contain the description of the operations. The description of each operation is as follows:
- 1 i: change the element with index ii to the opposite.
- 2 k: find the k-th one (ones are numbered from 0, it is guaranteed that there are enough ones in the array).
Output
For each operation of the second type, print the index of the corresponding one (all indices in this problem are from 0).
Example
input
5 7
1 1 0 1 0
2 0
2 1
2 2
1 2
2 3
1 0
2 0
output
0
1
3
3
1
问题解析
这题意思就是说,给你一个只有01组成的数组,有两种操作,第一个是把下标为x的元素取反(0变1,1变0),第二个操作是找到数组中的第x个1,让你输出这个的下标(比如数组是101011,那第三个1的下标就是4)。
线段树专题自然是线段树来写。先建树,线段树上的每个节点f[k]的意思是:区间l~r的1的个数。
单点修改操作对于学习过线段树的人来说并不难,这里便不再多说。重点是询问,我们该怎么利用线段树快速找到第x个1的下标。
我们每次先从根节点开始,比较两个孩子的1的个数,如果左孩子的1的个数小于x,说明第x个1应该出现在右区间,反之应该出现在左区间(如果去往右区间,那么x的值要减去左区间1的数量),当走到叶子节点时,得到的就该是第x个1的下标,我们返回并输出。
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#define endl '\n';
typedef long long ll;
typedef pair<int, int> PII;
const int N = 100050;
int f[4 * N], a[N];
void buildtree(int k,int l,int r)
{
if (l == r)
{
f[k] = a[l];
return;
}
int m = (l + r) / 2;
buildtree(k + k, l, m);
buildtree(k + k + 1, m+1, r);
f[k] = f[k + k] + f[k + k + 1];
}
void revise(int k, int l, int r, int x)
{
if (l == r)
{
f[k] = (f[k] == 1 ? 0 : 1);
return;
}
int m = (l + r) / 2;
if (x <= m)revise(k + k, l, m, x);
else revise(k + k + 1, m + 1, r, x);
f[k] = f[k + k] + f[k + k + 1];
}
int quire(int k, int l, int r, int x)
{
if (l == r)return l;
int m = (l + r) / 2;
if (f[k + k] >= x)return quire(k + k, l, m, x);
else return quire(k + k + 1, m + 1, r, x - f[k + k]);
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)cin >> a[i];
buildtree(1, 1, n);
while (m--)
{
int st, x;
cin >> st >> x;
if (st == 1)revise(1, 1, n, x+1);
else cout << quire(1, 1, n, x+1)-1 << endl;
}
return 0;
}