题解 P2367 【语文成绩】

· · 题解

这道题的输入规模非常大,老师肯定累垮了。

可以普及一下 fread。我之前没学,觉得它一定很复杂,其实它非常单纯。

普通快读

有这样的方法,用它读入整数比 scanf 快,帮你省下时间。

读入一个字符,如果是不是数字就继续读入。

读到一个数字?开始处理:

while(isdigit(ch))//判断是不是数字字符,此函数在头文件<cctype>中
    result = result * 10 + (ch - '0'), ch = getchar();

比如读入数字 408,分成几个字符读入:

小提示:数字字符要转变为数字,即减去字符 '0'(ASCII码是 48)。

int getint()//名字仿照了getchar()
{
    int res = 0;
    char ch = getchar();
    while(!isdigit(ch))
        ch = getchar();
    while(isdigit(ch))
        res = res * 10 + (ch - '0'), ch = getchar();
    return res;
}

用 fread 优化

只是优化了上面的 getchar(),因为 getchar() 有点慢。

只要在原来的快读里面把 getchar() 替换成 getch(),就快了许多了!

fread 讲完了。

细节

更多细节,如 getint() 判断负数、fread() 指针写法以及快速输出 fwrite(),感兴趣可看看快读有多难?

原题

先不看题目。

有一个概念叫做前缀和。现在有一个数组 t,里面都是 0。然后,我们用 sum[i] 表示 t 数组前 i 位的和。

如果 t[l] 增加 x,t[r+1] 减去 x,那么 sum[l] ~ sum[r] 都增加 x。

大量的区间加,最后询问所有数中的最小值。

我们可以开一个 t 数组,若修改学生 x ~ y 都加 z,就只在 t 数组上进行两个操作:t[x] 加上 z,t[y+1] 减去 z。你可以脑补出此时有一个 sum 数组,是 t 的前缀和,sum[i] 能正确表示第 i 个学生被修改了多少。但是不要每次修改 sum 数组,而是修改以 t 数组代替。

最后从左到右求一次 t 的前缀和,还原出 sum 数组,加到第 i 位时的 sum 就能表示出这个学生被修改的量。别忘了加上初始成绩,取最小。

t 叫做 sum 的差分数组。t 的前缀和就是 sum。

#include <cstdio>
#include <cctype>
char tmp[1000010];
int cnt = 0, Max = 0;
char getch()
{
    ++cnt;
    if(cnt > Max)
    {
        cnt = 1;
        Max = fread(tmp+1, 1, 1000000, stdin);
    }
    return tmp[cnt];
}
int getint()
{
    int res = 0;
    char ch = getch();
    while(!isdigit(ch))
        ch = getch();
    while(isdigit(ch))
        res = res * 10 + (ch - '0'), ch = getch();
    return res;
}

int n, p;
int a[5000010], b[5000010];
int main()
{
    n = getint(), p = getint();
    for(int i = 1; i <= n; i++)
        a[i] = getint();
    for(int i = 1; i <= p; i++)
    {
        int x = getint(), y = getint(), z = getint();
        b[x] += z;
        b[y+1] -= z;
    }

    int sum = 0, ans = 1000000000;
    for(int i = 1; i <= n; i++)
    {
        sum += b[i];
        if(a[i] + sum < ans)
            ans = a[i] + sum;
    }
    printf("%d\n", ans);
    return 0;
}