洛谷 P2122 还教室

1 P2122 还教室

2 题目描述

时间限制 \(1s\) | 空间限制 \(125M\)

在接受借教室请求的 \(n\) 天中,第 \(i\) 天剩余的教室为 \(a_i\) 个。作为大学借教室服务的负责人,你需要完成如下三种操作共 \(m\) 次:

  • ① 第 \(l\) 天到第 \(r\) 天,每天被归还 \(d\) 个教室。
  • ② 询问第 \(l\) 天到第 \(r\) 天教室个数的平均数。
  • ③ 询问第 \(l\) 天到第 \(r\) 天教室个数的方差。

对于每个操作 2 和操作 3,输出一个既约分数(分子与分母互质)表示询问的答案。若答案为 0,请输出“0/1”(不含引号)。

数据范围:\(1≤l≤r≤n≤10^5,m≤10^5,0 \leq a_i \leq 10,1 \leq d ≤3\)

3 题解

区间平均数极其好求,区间和即可。

我们来看区间方差的代数式:\(s^2 = \dfrac{1}{n} \sum_{i = 1}^{n}\limits (a_i - \bar x)^2\)

展开得:\(s^2 = \dfrac{1}{n} (\sum_{i = 1}^{n} \limits a_i^2 - 2 \times \sum_{i = 1}^{n} \limits a_i \bar x + \sum_{i = 1}^{n}\limits \bar x ^ 2)\)

显然,\(\sum_{i = 1}^{n} \limits a_i^2\) 可以用区间平方之和维护。我们可以把 \(\bar x = \dfrac{1}{n} \sum_{i = 1}^{n} \limits a_i\) 带入,得到:

\(2\times \sum_{i = 1}^{n} \limits a_i \bar x = \dfrac{2}{n} \times (\sum_{i = 1}^{n} \limits a_i) ^ 2\)

\(\sum_{i = 1}^{n} \limits \bar x ^ 2 = \dfrac{1}{n} \times (\sum_{i = 1}^{n} \limits a_i) ^ 2\)

因此,我们只需要知道区间的平方之和和区间和就可以求出答案。此时答案为 \(\dfrac{1}{n} (\sum_{i = 1}^{n} \limits a_i ^2 - \dfrac{1}{n} \times (\sum_{i = 1}^{n} \limits a_i)^2)\),将分母统一后(题目要求输出最简分数),答案为 \(\dfrac{1}{n^2} (n \times \sum_{i = 1}^{n} \limits a_i ^ 2 - (\sum_{i = 1}^{n} \limits a_i)^2)\)

接下来我们来思考怎么维护区间平方和。

如果我们已经知道某一段区间的平方和,此时我们往区间内每一个数上加上 \(d\),平方和为:

\(\sum_{i = 1}^{n} \limits (a_i + d)^2 = \sum_{i-1}^{n} \limits a_i ^2 + 2d \times \sum_{i = 1} ^n \limits a_i + nd^2\)

我们发现,这个式子的第一部分我们已经知道了,而第二部分我们可以通过维护区间和得到,最后一部分更是可以直接求出。

最后求一下分子和分母的最大公因数,输出即可。

4 代码(空格警告):

#include <iostream>
using namespace std;
const int N = 1e5+10;
#define int long long
int n, m, sum, sums, len, opt, l, r, k, up, down, Gcd;
int a[N];
struct node
{
    int l, r, sum, sums, tag;
}t[N*4];
int gcd(int x, int y)
{
    if (!y) return x;
    return gcd(y, x % y);
}
void pushup(int p)
{
    t[p].sum = t[p*2].sum + t[p*2+1].sum;
    t[p].sums = t[p*2].sums + t[p*2+1].sums;
}
void build(int p, int l, int r)
{
    t[p].l = l;
    t[p].r = r;
    if (l == r)
    {
        cin >> t[p].sum;
        t[p].sums = t[p].sum * t[p].sum;
        return ;
    }
    int mid = (l+r)/2;
    build(p*2, l, mid);
    build(p*2+1, mid+1, r);
    pushup(p);
}
void pushdown(int p)
{
    if (t[p].tag)
    {
        t[p*2].sums += 2 * t[p].tag * t[p*2].sum + t[p].tag * t[p].tag * (t[p*2].r - t[p*2].l + 1);
        t[p*2+1].sums += 2 * t[p].tag * t[p*2+1].sum + t[p].tag * t[p].tag * (t[p*2+1].r - t[p*2+1].l + 1);
        t[p*2].sum += (t[p*2].r - t[p*2].l + 1) * t[p].tag;
        t[p*2+1].sum += (t[p*2+1].r - t[p*2+1].l + 1) * t[p].tag;
        t[p*2].tag += t[p].tag;
        t[p*2+1].tag += t[p].tag;
        t[p].tag = 0;
    }
}
void modify(int p, int l, int r, int d)
{
    if (t[p].l >= l && t[p].r <= r) 
    {
        t[p].sums += 2 * d * t[p].sum + d * d * (t[p].r - t[p].l + 1);
        t[p].sum += (t[p].r - t[p].l + 1) * d;
        t[p].tag += d;
        return ;
    }
    pushdown(p);
    int mid = (t[p].l + t[p].r) / 2;
    if (l <= mid) modify(p*2, l, r, d);
    if (r > mid) modify(p*2+1, l, r, d);
    pushup(p);
}
int query1(int p, int l, int r)
{
    if (t[p].l >= l && t[p].r <= r) return t[p].sum;
    pushdown(p);
    int mid = (t[p].l + t[p].r) / 2, ans = 0;
    if (l <= mid) ans += query1(p*2, l, r);
    if (r > mid) ans += query1(p*2+1, l, r);
    return ans;
}
int query2(int p, int l, int r)
{
    if (t[p].l >= l && t[p].r <= r) return t[p].sums;
    pushdown(p);
    int mid = (t[p].l + t[p].r) / 2, ans = 0;
    if (l <= mid) ans += query2(p*2, l, r);
    if (r > mid) ans += query2(p*2+1, l, r);
    return ans;
}
signed main()
{
    cin >> n >> m;
    build(1, 1, n);
    for (int i = 1; i <= m; i++)
    {
        cin >> opt >> l >> r;
        if (opt == 1)
        {
            cin >> k;
            modify(1, l, r, k);
        }
        else if (opt == 2)
        {
            up = query1(1, l, r);
            down = r - l + 1;
            Gcd = gcd(up, down);
            up /= Gcd;
            down /= Gcd;
            cout << up << "/" << down << '\n';
        }
        else
        {
            sum = query1(1, l, r);
            sums = query2(1, l, r);
            len = r - l + 1;
            sums *= len;
            sum *= sum;
            up = sums - sum;
            down = len * len;
            Gcd = gcd(up, down);
            up /= Gcd;
            down /= Gcd;
            cout << up << "/" << down << '\n';
        }
    }
    return 0;
}

欢迎关注我的微信公众号:智子笔记

posted @ 2021-04-29 23:27  David24  阅读(46)  评论(0编辑  收藏  举报