2024.9.25校测

T1

题目描述

给你一个序列:a1,a2,a3,,an,有 m 个操作,操作如下:

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

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

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

输入格式

12 个整数:n,m,表示序列长的和操作数。

2n 个整数:a1,a2,a3,,an,表示初始序列。

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

输出格式

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

输入样例

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

输出样例

10

数据规模

对于 30% 数据,1n,m103

对于 100% 数据,1n,m105,1ai,xn,1lrn

题解

典型的线段树题。

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

完整代码

#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 个矩形,求它们的面积并。

更准确一点,每个矩形将给出它的左上角和右下角的位置:x1,y1,x2,y2

这四个数都是整数且满足 x1x2,y1y2

我们需要你求:

area={(x,y)Z×Zarect.s.t,x1xx2,y1yy2}

输入格式

11 个整数:n,表示矩形的个数。

接下来 n 行,每行 4 个整数:x1,y1,x2,y2,表示一个矩形的左上角和右下角的坐标。

输出格式

输出 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% 数据,1n100,1x1x2100,1y1y2100

对于 100% 数据,1n105,1x1x2105,1y1y2105

题解

非常模板的扫描线算法,如不知道什么是扫描线,请参考线段树进阶应用学习笔记(一)(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

输入格式

12 个整数:n,m,表示序列长度和操作数。

2n 个整数:a1,a2,a3,,an,表示给定序列。

接下来 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

提示

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

数据规模

对于 30% 数据,1n,m103

对于 100% 数据,1n,m105,1ai,u,xn,1krl+1,1lrn

题解

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

先考虑朴素二分,从 infinf 二分答案,统计小于 mid 的数的个数,如果 kcnt,那么就往左区间二分,否则往右区间二分,最后当 l=rl 就是答案。

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

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

完整代码

#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 @   JPGOJCZX  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示