2024.9.25校测

T1

题目描述

给你一个序列:\(a_1, a_2, a_3, \dots, a_n\),有 \(m\) 个操作,操作如下:

  • modify l r x,将区间 \([l, r]\) 中的每个数修改为 \(x\)

  • change l r x,将区间 \([l, r]\) 中的每个数加上 \(x\)

  • query l r,询问区间 \([l, r]\) 中的和。

输入格式

\(1\)\(2\) 个整数:\(n, m\),表示序列长的和操作数。

\(2\)\(n\) 个整数:\(a_1, a_2, a_3, \dots, a_n\),表示初始序列。

接下来 \(m\) 行,每行是上面三种操作中的一种。

输出格式

对于每个询问操作,输出其结果。

输入样例

3 3
1 2 3
change 1 3 2
modify 3 3 3
query 1 3

输出样例

10

数据规模

对于 \(30\%\) 数据,\(1 \leq n, m \leq 10^3\)

对于 \(100\%\) 数据,\(1 \leq n, m \leq 10^5, 1 \leq a_i, x \leq n, 1 \leq l \leq r \leq n\)

题解

典型的线段树题。

考虑一个节点有加标记,要添加改标记,就将加标记清零;如果一个节点有改标记,要添加加标记,直接在原加标记上增加就行了。

完整代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4e5 + 9;
int n, m, t[N << 2], tag_add[N << 2], tag_change[N << 2];
char ch[20];
void buildTree(int id, int L, int R){
	if(L == R){
		scanf("%lld", &t[id]);
		return;
	}
	int mid = (L + R) >> 1;
	buildTree(id << 1, L, mid);
	buildTree(id << 1 | 1, mid + 1, R);
	t[id] = t[id << 1] + t[id << 1 | 1];
}
void pushdown(int id, int L, int R){
	int mid = (L + R) >> 1;
	if(tag_change[id]){
		tag_add[id << 1] = tag_add[id << 1 | 1] = 0;
		t[id << 1] = tag_change[id] * (mid - L + 1);
		t[id << 1 | 1] = tag_change[id] * (R - mid);
		tag_change[id << 1] = tag_change[id << 1 | 1] = tag_change[id];
		tag_change[id] = 0;
	}
	if(tag_add[id]){
		tag_add[id << 1] += tag_add[id];
		t[id << 1] += tag_add[id] * (mid - L + 1);
		tag_add[id << 1 | 1] += tag_add[id];
		t[id << 1 | 1] += tag_add[id] * (R - mid);
		tag_add[id] = 0;
	}
}
void modify(int id, int L, int R, int qL, int qR, int qx, int type){
	pushdown(id, L, R);
	if(L == qL && R == qR){
		if(type == 1){
			tag_add[id] = 0;
			t[id] = qx * (R - L + 1);
			tag_change[id] = qx;
			return;
		} else {
			t[id] += qx * (R - L + 1);
			tag_add[id] += qx;
			return;
		}
	}
	int mid = (L + R) >> 1;
	if(qR <= mid)
		modify(id << 1, L, mid, qL, qR, qx, type);
	else if(qL > mid)
		modify(id << 1 | 1, mid + 1, R, qL, qR, qx, type);
	else{
		modify(id << 1, L, mid, qL, mid, qx, type);
		modify(id << 1 | 1, mid + 1, R, mid + 1, qR, qx, type);
	}
	t[id] = t[id << 1] + t[id << 1 | 1];
}
int query(int id, int L, int R, int qL, int qR){
	pushdown(id, L, R);
	if(L == qL && R == qR)
		return t[id];
	int mid = (L + R) >> 1;
	if(qR <= mid)
		return query(id << 1, L, mid, qL, qR);
	else if(qL > mid)
		return query(id << 1 | 1, mid + 1, R, qL, qR);
	else
		return query(id << 1, L, mid, qL, mid) + query(id << 1 | 1, mid + 1, R, mid + 1, qR);
}
signed main(){
	freopen("setmod.in", "r", stdin);
	freopen("setmod.out", "w", stdout);
	scanf("%lld%lld", &n, &m);
	buildTree(1, 1, n);
	for(int i = 1; i <= m; ++i){
		int qL, qR, qx;
		scanf("%s", ch);
		if(ch[0] == 'c'){
			scanf("%lld%lld%lld", &qL, &qR, &qx);
			if(qL > qR)
				continue;
			modify(1, 1, n, qL, qR, qx, 2);
		} else if(ch[0] == 'm'){
			scanf("%lld%lld%lld", &qL, &qR, &qx);
			if(qL > qR)
				continue;
			modify(1, 1, n, qL, qR, qx, 1);
		} else {
			scanf("%lld%lld", &qL, &qR);
			if(qL > qR){
				printf("0\n");
				continue;
			}	
			printf("%lld\n", query(1, 1, n, qL, qR));
		}
	}
	return 0;
}

T2

题目描述

给出 \(n\) 个矩形,求它们的面积并。

更准确一点,每个矩形将给出它的左上角和右下角的位置:\(x_1, y_1, x_2, y_2\)

这四个数都是整数且满足 \(x_1 \leq x_2, y_1 \leq y_2\)

我们需要你求:

\(area = \{(x, y) \in Z \times Z \mid \exists a \, rect.s.t, x_1 \leq x \leq x_2, y_1 \leq y \leq y_2\}\)

输入格式

\(1\)\(1\) 个整数:\(n\),表示矩形的个数。

接下来 \(n\) 行,每行 \(4\) 个整数:\(x_1, y_1, x_2, y_2\),表示一个矩形的左上角和右下角的坐标。

输出格式

输出 \(area\)

输入样例

3
1 1 2 3
1 2 3 3
3 3 4 4

输出样例

11

样例解释

一共有 \(11\) 个点落在了上面三个矩形所表示的区域内:
\((1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 2), (3, 3), (3, 4), (4, 3), (4, 4)\)

数据规模

对于 \(30\%\) 数据,\(1 \leq n \leq 100, 1 \leq x_1 \leq x_2 \leq 100, 1 \leq y_1 \leq y_2 \leq 100\)

对于 \(100\%\) 数据,\(1 \leq n \leq 10^5, 1 \leq x_1 \leq x_2 \leq 10^5, 1 \leq y_1 \leq y_2 \leq 10^5\)

题解

非常模板的扫描线算法,如不知道什么是扫描线,请参考线段树进阶应用学习笔记(一)(2024.7.19)(2024.8.22)

完整代码

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9;
struct Node{
	int l, r, y, op;
} a[N << 1];
bool cmp(Node a, Node b){
	return a.y < b.y;
}
int t[N << 2], tag[N << 2], ans, cnt, n;
void pushup(int id, int L, int R){
	if(tag[id]){
		t[id] = R - L + 1;
		return;
	}	
	t[id] = t[id << 1] + t[id << 1 | 1];
}
void modify(int id, int L, int R, int qL, int qR, int qx){
	if(qL == L && R == qR){
		tag[id] += qx;
		pushup(id, L, R);
		return;
	}
	int mid = (L + R) >> 1;
	if(qR <= mid)
		modify(id << 1, L, mid, qL, qR, qx);
	else if(qL > mid)
		modify(id << 1 | 1, mid + 1, R, qL, qR, qx);
	else {
		modify(id << 1, L, mid, qL, mid, qx);
		modify(id << 1 | 1, mid + 1, R, mid + 1, qR, qx);
	}
	pushup(id, L, R);
}
signed main(){
//	freopen("area.in", "r", stdin);
//	freopen("area.out", "w", stdout);
	scanf("%lld", &n);
	for(int i = 1; i <= n; i++){
		int x1, y1, x2, y2;
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		a[++cnt] = {x1, x2, y1, 1};
		a[++cnt] = {x1, x2, y2 + 1, -1};
	}
	sort(a + 1, a + cnt + 1, cmp);
	for(int i = 1; i <= cnt; i++){
		ans += t[1] * (a[i].y - a[i - 1].y);
		modify(1, 1, N, a[i].l, a[i].r, a[i].op);
	}
	printf("%lld", ans);
	return 0;
}

T3

题目描述

我看好你哟。

给你一个长度为 n 的序列,有 m 个操作:

  • modify u x 将第 \(u\) 个数修改为 \(x\)

  • query l r k 询问区间 \([l, r]\) 中第 \(k\) 小的数\(^1\)

输入格式

\(1\)\(2\) 个整数:\(n, m\),表示序列长度和操作数。

\(2\)\(n\) 个整数:\(a_1, a_2, a_3, \dots,a_n\),表示给定序列。

接下来 \(m\) 行,每行表示上面的某个操作。

输出格式

对于每个询问操作,输出其结果。

输入样例

5 5
5 2 1 3 4
query 1 4 3
modify 4 5
query 1 4 3
modify 1 3
query 1 4 3

输出样例

3
5
3

提示

\(^1\)\(k\) 小是指将区间 \([l, r]\) 从小到大排序后,第 \(k\) 个数。

数据规模

对于 \(30\%\) 数据,\(1 \leq n, m \leq 10^3\)

对于 \(100\%\) 数据,\(1 \leq n, m \leq 105, 1 \leq a_i, u, x \leq n, 1 \leq k \leq r − l + 1, 1 \leq l \leq r \leq n\)

题解

区间第 \(k\) 小,考虑整体二分。

先考虑朴素二分,从 \(-inf\)\(inf\) 二分答案,统计小于 \(mid\) 的数的个数,如果 \(k \geq cnt\),那么就往左区间二分,否则往右区间二分,最后当 \(l = r\)\(l\) 就是答案。

那么整体二分就是将几个询问一起二分,首先将询问离线,将 \(k \geq cnt\) 的数放在存储数组的左边,将 \(k < cnt\) 的数放在存储数组的右边,分别二分左边和右边,最后当 \(l = r\)\(l\) 就是存储数组在 \(ql\)\(qr\) 区间的所有询问的答案。

考虑如何修改,考虑值域树状数组,将一个数改为另一个数时,只用将原先的数在树状数组中减 \(1\),将修改后的数在树状数组中加 \(1\),这也可以使,统计小于 \(mid\) 的数的个数的复杂度降低到 \(O(\log n)\),总复杂度就是 \(O(n \log^2 n)\)

完整代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 9, inf = 1e9 + 7;
struct node{
    int x, y, k, type, id;
} q[N * 3], q1[N * 3], q2[N * 3];
int n, m, tot, totx;
int a[N], ans[N];
int c[N * 24];
int lowbit(int p){
    return (p & -p);
}  
void Add(int x, int v){
    if(!x)
        return;
    while(x < 500005)
        c[x] += v, x += lowbit(x);
}
int Ask(int x){
    int t = 0;
    while(x)
        t += c[x], x -= lowbit(x);
    return t;
}
int Ask(int l, int r){
    if (l > r)
        return 0;
    return Ask(r) - Ask(l - 1);
}
void solve(int ql, int qr, int L, int R){
    if(ql > qr)
		return;
    if(L == R){
        for(int i = ql; i <= qr; i++)
            if(q[i].type == 2)
                ans[q[i].id] = L;
        return;
    }
    int mid = (L + R) >> 1;
    int t1 = 0, t2 = 0;
    for(int i = ql; i <= qr; i++){
        if(q[i].type == 1){
            if(q[i].x <= mid){
                Add(q[i].id, q[i].y);
                q1[t1++] = q[i];
            } else
                q2[t2++] = q[i];
        } else {
            int tt = Ask(q[i].x, q[i].y);
            if(tt >= q[i].k)
				q1[t1++] = q[i];
            else {
                q[i].k -= tt;
                q2[t2++] = q[i];
            }
        }
    }
    for(int i = 0; i < t1; i++)
		q[ql + i] = q1[i];
    for(int i = 0; i < t2; i++)
		q[ql + t1 + i] = q2[i];
    for(int i = 0; i < t1; i++)
        if(q1[i].type == 1)
            Add(q1[i].id, -q1[i].y);
    solve(ql, ql + t1 - 1, L, mid);
    solve(ql + t1, qr, mid + 1, R);
}
signed main(){
	freopen("intkth.in", "r", stdin);
	freopen("intkth.out", "w", stdout);
    scanf("%lld%lld", &n, &m);
    int x, y, k;
    char op[5];
    for(int i = 1; i <= n; i++){
        scanf("%lld", &a[i]);
        tot++;
        q[tot].x = a[i], q[tot].y = 1, q[tot].type = 1, q[tot].id = i;
	}
    for(int i = 1; i <= m; i++){
        scanf("%s", op);
        if(op[0] == 'q'){
            scanf("%lld%lld%lld", &x, &y, &k);
            tot++;
            q[tot].x = x, q[tot].y = y, q[tot].k = k, q[tot].type = 2, q[tot].id = ++totx;
        } else {
            scanf("%lld%lld", &x, &y);
            tot++;
            q[tot].x = a[x]; q[tot].y = -1; q[tot].type = 1, q[tot].id = x;
            tot++;
            a[x] = y;
            q[tot].x = a[x]; q[tot].y = 1; q[tot].type = 1, q[tot].id = x;
        }
    }
    solve(1, tot, 0, inf);
    for(int i = 1; i <= totx; i++)
        printf("%lld\n", ans[i]);
    return 0;
}
posted @ 2024-09-25 17:33  Orange_new  阅读(45)  评论(0)    收藏  举报