• 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,mle10^5 n,m105

    题解

    • 显然 a i ≥ i a_ige i aii是没有用的。
    • 考虑某个点 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
    ", t == -1 ? i : t);
    		}	
    	}
    	return 0;
    }
    

    自我小结

    • 形象的题意转化并不难,但考场并没有想到。
    • 可以尝试多手玩样例,便会发现很多结论都很显然。
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    改善深层神经网络
    IO操作 第一篇 学习(转载)
    杂谈:收集的一些博文
    杭电2072
    Java数组常用方法
    JAVA中final修饰符小结
    南阳106
    南阳283
    南阳277
    南阳458
  • 原文地址:https://www.cnblogs.com/LZA119/p/14437603.html
Copyright © 2020-2023  润新知