【YBTOJ】【Luogu P2617】Dynamic Rankings
链接:
题目大意:
给定一个含有 \(n\) 个数的序列 \(a_1,a_2, \dots, a_n\),需要支持两种操作:
Q l r k
表示查询下标在区间 \([l,r]\) 中的第 \(k\) 小的数。C x y
表示将 \(a_x\) 改为 \(y\)。
正文:
和静态区间第 \(k\) 小的思路基本相同,详见 可持久化数据结构。而不同的是,因为变成动态的,原来的主席树维护的前缀和不能再这么维护了,考虑用树状数组维护那个前缀和:在跑权值线段树的时候分别开两个树状数组维护前缀和,分别表示 \([1,l-1]\) 区间和与 \([1,r]\) 区间和,显然通过这两个树状数组能得到 \([l,r]\) 前缀和。
其余操作和静态区间第 \(k\) 小就一样了,然而还有一点需要注意,这道题需要卡常,请注意你的常数问题。
代码:
const int N = 100010;
inline ll read()
{
char c;ll f = 0, d = 1;
while(c = getchar(), !isdigit(c)) if(c == '-') d=-1;f=(f<<3)+(f<<1)+c-48;
while(c = getchar(), isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
int n, m, nn;
ll a[N], b[N << 1]; int root[N * 600];
ll inp[N][3], t[2][N], cntl, cntr;
struct SegandTree_arrar
{
int lt[N * 600], rt[N * 600], cnt;
ll sum[N * 600];
void change(int &x, int l, int r, int p, int val)
{
if (!x) x = ++cnt; sum[x] += val;
if (l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) change(lt[x], l, mid, p, val);
else change (rt[x], mid + 1, r, p, val);
}
int query(int l, int r, int k)
{
if (l == r) return l;
ll ans = 0;int mid = (l + r) >> 1;
for (int i = 1; i <= cntl; i++) ans -= sum[lt[t[0][i]]];
for (int i = 1; i <= cntr; i++) ans += sum[lt[t[1][i]]];
if (k <= ans)
{
for (int i = 1; i <= cntl; i++) t[0][i] = lt[t[0][i]];
for (int i = 1; i <= cntr; i++) t[1][i] = lt[t[1][i]];
return query(l, mid, k);
}
else
{
for (int i = 1; i <= cntl; i++) t[0][i] = rt[t[0][i]];
for (int i = 1; i <= cntr; i++) t[1][i] = rt[t[1][i]];
return query(mid + 1, r, k - ans);
}
}
}sgt;
void add(int pos, int val)
{
int p = lower_bound(b + 1, b + 1 + nn, a[pos]) - b;
for (; pos <= n; pos += pos & -pos)
sgt.change(root[pos], 1, nn, p, val);
}
int main()
{
n = read(), m = read();
for (int i = 1; i <= (nn=n); i++)
a[i] = read(), b[i] = a[i];
for (int i = 1; i <= m; i++)
{
char s[5];
scanf ("%s", s);
inp[i][0] = read(), inp[i][1] = read();
if (s[0] == 'Q') inp[i][2] = read();
else b[++nn] = inp[i][1];
}
sort(b + 1, b + 1 + nn);
nn = unique(b + 1, b + 1 + nn) - b - 1;
for (int i = 1; i <= n; i++) add(i, 1);
for (int i = 1; i <= m; i++)
{
if (inp[i][2])
{
cntl = cntr = 0;
int l = inp[i][0], r = inp[i][1];
for (int x = r; x; x -= x & -x) t[1][++cntr] = root[x];
for (int x = l - 1; x; x -= x & -x) t[0][++cntl] = root[x];
printf ("%lld\n", b[sgt.query(1, nn, inp[i][2])]);
}else
{
add(inp[i][0], -1);
a[inp[i][0]] = inp[i][1];
add(inp[i][0], 1);
}
}
return 0;
}