GDKOI 2021 提高组 Day2 第二题 群岛(线段树)
作者:互联网
GDKOI 2021 提高组 Day2 第二题 群岛
题目大意
- n n n个点,每个点 i i i只有连向 i + 1 i+1 i+1和 a i a_i ai两条出边, m m m次操作,支持修改 a i a_i ai,询问点 x x x能到达的编号最小的点。
- n , m ≤ 1 0 5 n,m\le10^5 n,m≤105
题解
- 显然 a i ≥ i a_i\ge i ai≥i是没有用的。
- 考虑某个点 x x x会以何种方式走向最优的点,不失一般性地,一定是先往右走若干格,再通过 a x a_x ax走向较小的点,然后如此循环重复。
- 这个过程中会使用多若干组 ( i , a i ) (i,a_i) (i,ai),它们能在一次询问中被同时使用到当且仅当存在另一组 ( a j , j ] (a_j,j] (aj,j]与 ( a i , i ] (a_i,i] (ai,i]的交不为空,形象的说即为这若干条左开右闭的线段都不能被不理,至少要有另一条与它有重叠。
- 那么它能走到的位置即为 x x x左边最靠右的一个未被线段覆盖的位置,可以用线段树维护。
- 具体地,记录每个区间的最小值及最右边的最小值所在的位置,区间合并显然。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
int a[N];
struct {
int r, mi, p;
}f[N * 4];
void make(int v, int l, int r) {
f[v].r = r, f[v].mi = 0;
if(l == r) return;
int mid = (l + r) / 2;
make(v * 2, l, mid), make(v * 2 + 1, mid + 1, r);
}
int find(int v, int l, int r, int x, int y) {
if(x > y) return -1;
if(l == x && r == y) {
if(f[v].mi == 0) return f[v].r;
return -1;
}
else {
int mid = (l + r) / 2;
f[v * 2].p += f[v].p, f[v * 2 + 1].p += f[v].p;
f[v * 2].mi += f[v].p, f[v * 2 + 1].mi += f[v].p;
f[v].p = 0;
int s;
if(y <= mid) s = find(v * 2, l, mid, x, y);
else if(x > mid) s = find(v * 2 + 1, mid + 1, r, x, y);
else {
int t = find(v * 2 + 1, mid + 1, r, mid + 1, y);
if(t != -1) s = t; else s = find(v * 2, l, mid, x, mid);
}
f[v].mi = min(f[v * 2].mi, f[v * 2 + 1].mi);
if(f[v * 2].mi < f[v * 2 + 1].mi) f[v].r = f[v * 2].r; else f[v].r = f[v * 2 + 1].r;
return s;
}
}
void ins(int v, int l, int r, int x, int y, int c) {
if(x > y) return;
if(l == x && r == y) f[v].mi += c, f[v].p += c;
else {
int mid = (l + r) / 2;
f[v * 2].p += f[v].p, f[v * 2 + 1].p += f[v].p;
f[v * 2].mi += f[v].p, f[v * 2 + 1].mi += f[v].p;
f[v].p = 0;
if(y <= mid) ins(v * 2, l, mid, x, y, c);
else if(x > mid) ins(v * 2 + 1, mid + 1, r, x, y, c);
else ins(v * 2, l, mid, x, mid, c), ins(v * 2 + 1, mid + 1, r, mid + 1, y, c);
f[v].mi = min(f[v * 2].mi, f[v * 2 + 1].mi);
if(f[v * 2].mi < f[v * 2 + 1].mi) f[v].r = f[v * 2].r; else f[v].r = f[v * 2 + 1].r;
}
}
int main() {
int n, Q, i, j;
scanf("%d%d", &n, &Q);
make(1, 1, n);
for(i = 1; i <= n; i++) {
scanf("%d", &a[i]);
ins(1, 1, n, a[i] + 1, i, 1);
}
while(Q--) {
int op, x, y;
scanf("%d", &op);
if(op == 1) {
scanf("%d%d", &x, &y);
ins(1, 1, n, a[x] + 1, x, -1);
a[x] = y;
ins(1, 1, n, a[x] + 1, x, 1);
}
else {
scanf("%d", &x);
int t = find(1, 1, n, 1, x);
printf("%d\n", t == -1 ? i : t);
}
}
return 0;
}
自我小结
- 形象的题意转化并不难,但考场并没有想到。
- 可以尝试多手玩样例,便会发现很多结论都很显然。
标签:return,int,mi,Day2,mid,else,2021,GDKOI,ins 来源: https://blog.csdn.net/qq_39565901/article/details/113485889