线段树专题三与浮点数精度问题

P2184 贪婪大陆

贪婪大陆

题目背景

面对蚂蚁们的疯狂进攻,小 FF 的 Tower defence 宣告失败……人类被蚂蚁们逼到了 Greed Island 上的一个海湾。现在,小 FF 的后方是一望无际的大海,前方是变异了的超级蚂蚁。小 FF 还有大好前程,他可不想命丧于此, 于是他派遣手下最后一批改造 SCV 布置地雷以阻挡蚂蚁们的进攻。

题目描述

小 FF 最后一道防线是一条长度为 n n n 的战壕,小 FF 拥有无数多种地雷,而 SCV 每次可以在 [ L , R ] [L, R] [L,R] 区间埋放同一种不同于之前已经埋放的地雷。由于情况已经十万火急,小 FF 在某些时候可能会询问你在 [ L ′ , R ′ ] [L',R'] [L,R] 区间内有多少种不同的地雷,他希望你能尽快的给予答复。

输入格式

第一行为两个整数 n n n m m m n n n 表示防线长度, m m m 表示 SCV 布雷次数及小 FF 询问的次数总和。

接下来有 m m m 行,每行三个整数 q , l , r q,l,r q,l,r

  • q = 1 q=1 q=1,则表示 SCV 在 [ l , r ] [l, r] [l,r] 这段区间布上一种地雷;
  • q = 2 q=2 q=2,则表示小 FF 询问当前 [ l , r ] [l, r] [l,r] 区间总共有多少种地雷。

输出格式

对于小 FF 的每次询问,输出一个答案(单独一行),表示当前区间地雷总数。

样例 #1

样例输入 #1

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

样例输出 #1

1
2

提示

数据规模与约定

  • 对于 30 % 30\% 30% 的数据, 0 ≤ n 0 \le n 0n m ≤ 1000 m \le 1000 m1000
  • 对于 100 % 100\% 100% 的数据, 0 ≤ n 0 \le n 0n m ≤ 1 0 5 m \le 10^5 m105

考虑每次加入地雷时,维护加入地雷区间开头的个数,和加入地雷结尾区间的个数。

对于每次询问 [l, r] 范围有多少地雷,答案就是总地雷数量 - s1 - s2

其中, s 1 s1 s1 表示结尾在 [ 1 , l − 1 ] [1,l-1] [1,l1] 的总数, s 2 s2 s2 表示开头在 [ r + 1 , n ] [r+1,n] [r+1,n] 的总数。

显然容斥掉上面两部分,剩余种类的地雷一定在 [l, r] 范围内有分布。

更通俗的来讲,想知道 [l, r] 范围里面的地雷总种类数,那么对于每种种类的地雷所在的范围区间,必须跟 [l, r] 有交集,上面容斥掉的就是两种没有交集的情况。

#include<bits/stdc++.h>
using namespace std;
#define int long long

int const N = 1e5 + 10;

#define lc u << 1
#define rc u << 1 | 1

struct node{
    int l, r, s1, s2;
}tr[N << 2];

void pushup(int u){
    tr[u].s1 = tr[lc].s1 + tr[rc].s1;
    tr[u].s2 = tr[lc].s2 + tr[rc].s2;
}

void build(int u, int l, int r){
    tr[u] = {l, r, 0, 0};
    if(l == r) return ;
    int mid = l + r >> 1;
    build(lc, l, mid);
    build(rc, mid + 1, r);
    pushup(u);
}

int askS1(int u, int l, int r){
    if(l <= tr[u].l && r >= tr[u].r){
        return tr[u].s1;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    int res = 0;
    if(l <= mid) res += askS1(lc, l, r);
    if(r > mid) res += askS1(rc, l, r);
    return res;
}

int askS2(int u, int l, int r){
    if(l <= tr[u].l && r >= tr[u].r){
        return tr[u].s2;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    int res = 0;
    if(l <= mid) res += askS2(lc, l, r);
    if(r > mid) res += askS2(rc, l, r);
    return res;
}

void modify(int u, int p, int tp){
    if(tr[u].l == tr[u].r){
        if(tp == 1) tr[u].s1 ++;
        else tr[u].s2 ++;
        return ;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if(p <= mid) modify(lc, p, tp);
    else modify(rc, p, tp);
    pushup(u);
}

void solve(){
    int n, m;
    cin >> n >> m;
    build(1, 1, n);
    int cnt = 0;
    for(int i = 1; i <= m; i ++){
        int op, l, r;
        cin >> op >> l >> r;
        if(op == 1){
            cnt ++;
            modify(1, l, 1);
            modify(1, r, 2);
        }
        else{
            int res = cnt;
            if(l != 1) res -= askS2(1, 1, l - 1);
            if(r != n) res -= askS1(1, r + 1, n);
            cout << res << '\n';
        }
    }
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int T = 1;
    while (T --){
        solve();
    }

    return 0;
}

P1471 方差

方差

题目背景

滚粗了的 HansBug 在收拾旧数学书,然而他发现了什么奇妙的东西。

题目描述

蒟蒻 HansBug 在一本数学书里面发现了一个神奇的数列,包含 N N N 个实数。他想算算这个数列的平均数和方差。

输入格式

第一行包含两个正整数 N , M N,M N,M,分别表示数列中实数的个数和操作的个数。

第二行包含 N N N 个实数,其中第 i i i 个实数表示数列的第 i i i 项。

接下来 M M M 行,每行为一条操作,格式为以下三种之一:

操作 1 1 11 x y k ,表示将第 x x x 到第 y y y 项每项加上 k k k k k k 为一实数。
操作 2 2 22 x y ,表示求出第 x x x 到第 y y y 项这一子数列的平均数。
操作 3 3 33 x y ,表示求出第 x x x 到第 y y y 项这一子数列的方差。

输出格式

输出包含若干行,每行为一个实数,即依次为每一次操作 2 2 2 或操作 3 3 3 所得的结果(所有结果四舍五入保留 4 4 4 位小数)。

样例 #1

样例输入 #1

5 5
1 5 4 2 3
2 1 4
3 1 5
1 1 1 1
1 2 2 -1
3 1 5

样例输出 #1

3.0000
2.0000
0.8000

提示

关于方差:对于一个有 n n n 项的数列 A A A,其方差 s 2 s^2 s2 定义如下:
s 2 = 1 n ∑ i = 1 n ( A i − A ‾ ) 2 s^2=\frac{1}{n}\sum\limits_{i=1}^n\left(A_i-\overline A\right)^2 s2=n1i=1n(AiA)2
其中 A ‾ \overline A A 表示数列 A A A 的平均数, A i A_i Ai 表示数列 A A A 的第 i i i 项。

样例说明:

操作步骤输入内容操作要求数列输出结果说明
0 0 0--1 5 4 2 3--
1 1 12 1 4 [ 1 , 4 ] \left[1,4\right] [1,4] 内所有数字的平均数1 5 4 2 33.0000平均数 = ( 1 + 5 + 4 + 2 ) ÷ 4 = 3.0000 =\left(1+5+4+2\right)\div 4=3.0000 =(1+5+4+2)÷4=3.0000
2 2 23 1 5 [ 1 , 5 ] \left[1,5\right] [1,5] 内所有数字的方差1 5 4 2 32.0000平均数 = ( 1 + 5 + 4 + 2 + 3 ) ÷ 5 = 3 =\left(1+5+4+2+3\right)\div 5=3 =(1+5+4+2+3)÷5=3,方差 = ( ( 1 − 3 ) 2 + ( 5 − 3 ) 2 + ( 4 − 3 ) 2 + ( 2 − 3 ) 2 + ( 3 − 3 ) 2 ) ÷ 5 = 2.0000 =\left(\left(1-3\right)^2+\left(5-3\right)^2+\left(4-3\right)^2+\left(2-3\right)^2+\left(3-3\right)^2\right)\div 5=2.0000 =((13)2+(53)2+(43)2+(23)2+(33)2)÷5=2.0000
3 3 31 1 1 1 [ 1 , 1 ] \left[1,1\right] [1,1] 内所有数字加 1 1 12 5 4 2 3--
4 4 41 2 2 -1 [ 2 , 2 ] \left[2,2\right] [2,2] 内所有数字加 − 1 -1 12 4 4 2 3--
5 5 53 1 5 [ 1 , 5 ] \left[1,5\right] [1,5] 内所有数字的方差2 4 4 2 30.8000平均数 = ( 2 + 4 + 4 + 2 + 3 ) ÷ 5 = 3 =\left(2+4+4+2+3\right)\div 5=3 =(2+4+4+2+3)÷5=3,方差 = ( ( 2 − 3 ) 2 + ( 4 − 3 ) 2 + ( 4 − 3 ) 2 + ( 2 − 3 ) 2 + ( 3 − 3 ) 2 ) ÷ 5 = 0.8000 =\left(\left(2-3\right)^2+\left(4-3\right)^2+\left(4-3\right)^2+\left(2-3\right)^2+\left(3-3\right)^2\right)\div 5=0.8000 =((23)2+(43)2+(43)2+(23)2+(33)2)÷5=0.8000

数据规模:

数据点 N N N M M M备注
1 ∼ 3 1\sim3 13 N ≤ 8 N\le 8 N8 M ≤ 15 M\le 15 M15-
4 ∼ 7 4\sim7 47 N ≤ 1 0 5 N\le 10^5 N105 M ≤ 1 0 5 M\le 10^5 M105不包含操作 3 3 3
8 ∼ 10 8\sim10 810 N ≤ 1 0 5 N\le 10^5 N105 M ≤ 1 0 5 M\le 10^5 M105-

方差 D = ∑ i = 1 n ( x i − x ‾ ) 2 n D = \frac {\sum_{i=1}^n(x_i-\overline x)^2}{n} D=ni=1n(xix)2

根据这个公式维护线段树即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long

int const N = 5e4 + 10;
int const inf = 2e18;

#define lc u << 1
#define rc u << 1 | 1

struct node{
	int l, r, ln;
	int pre, suf, len; // 0 的连续最长前后缀以及最长 0 的连续串
	int tag; // 是否区间修改为 0
}tr[N << 2];

void pushup(node &u, node &lson, node &rson){
	u.l = lson.l, u.r = rson.r;
	u.ln = lson.ln + rson.ln;
	u.pre = lson.pre == lson.ln ? lson.pre + rson.pre : lson.pre;
	u.suf = rson.suf == rson.ln ? rson.suf + lson.suf : rson.suf;
	u.len = max(max(lson.len, rson.len), lson.suf + rson.pre);
}

void pushup(int u){
	pushup(tr[u], tr[lc], tr[rc]);
}

void build(int u, int l, int r){
	tr[u] = {l, r, 1, 1, 1, 1, inf}; // 默认一开始全是 0
	if(l == r) return ;
	int mid = l + r >> 1;
	build(lc, l, mid);
	build(rc, mid + 1, r);
	pushup(u);
}

void pushdown(int u, int tag){
	if(tag != inf){
		tr[u].pre = tr[u].suf = tr[u].len = tr[u].ln * (tag == 0);
		tr[u].tag = tag;
	}
}

void pushdown(int u){
	pushdown(lc, tr[u].tag);
	pushdown(rc, tr[u].tag);
	tr[u].tag = inf;
}

// a[l ~ r] = v
void segModify(int u, int l, int r, int v){
	if(l <= tr[u].l && r >= tr[u].r){
		pushdown(u, v);
		return ;
	}
	int mid = tr[u].l + tr[u].r >> 1;
	pushdown(u);
	if(l <= mid) segModify(lc, l, r, v);
	if(r > mid) segModify(rc, l, r, v);
	pushup(u);
}

// 在线段 u 上找到出现 x 个连续 0 的最左边的编号
int segAsk(int u, int x){
	if(tr[u].l == tr[u].r){ // 搜索到叶子节点
		return tr[u].l;
	}
	int mid = tr[u].l + tr[u].r >> 1;
	pushdown(u);
	if(tr[lc].len >= x) return segAsk(lc, x);
	else if(tr[lc].suf + tr[rc].pre >= x) return mid - tr[lc].suf + 1;
	else return segAsk(rc, x);
	pushup(u);
}

int n, m, op, x, y;

void solve(){
	cin >> n >> m;
	build(1, 1, n); 
	while(m --){
		cin >> op >> x;
		if(op == 1){
			if(tr[1].len < x){
				cout << "0\n";
				continue ;
			}
			int l = segAsk(1, x);
			int r = l + x - 1;
			cout << l << '\n';
			segModify(1, l, r, 1);
		}
		else{
			cin >> y;
			segModify(1, x, x + y - 1, 0);
		}
	}
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	while (T --){
		solve();
	}

	return 0;
}

浮点数精度问题

不管是 C 风格的 printf 还是 C++ 的 setprecision() 都会浮点数进行四舍五入后输出。

#include<bits/stdc++.h>
using namespace std;

int main(){
	// setprecison 和 printf 都会对浮点数进行四舍五入后输出
	double x = 1.8888;
	cout << fixed << setprecision(3) << x << '\n';
	printf("%.3lf\n", x);
 
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值