{ }codelper
前缀和64 位整数P0307

越远越凉的篝火与营地的温暖

营地有n个帐篷位排成一排,编号从1到n。
点起m堆篝火。
同一个位置可以有多堆篝火。
在p号位置以强度s点起的篝火,使x号位置增加s − |x − p|的温暖。
这个值不大于0时,该位置不会被这堆篝火温暖。
也就是p号位置得到s,两边相邻的位置得到s − 1,再远一格得到s − 2,每远一格减1。
1号左边和n号右边没有位置,往那边去的温暖就丢掉了。
一个位置的温暖是所有篝火给它的值之和。
第一行输出最温暖的位置的温暖值和它的编号。
这样的位置有多个时,选编号最小的。
接着依次回答q个询问。
询问“l r”的答案是l号到r号位置的温暖之和。

输入
第一行是n、m、q,接下来m行是篝火的“p s”,再接下来q行是询问的“l r”,用一个空格隔开。
1 ≤ n ≤ 9,000,1 ≤ m ≤ 10,000,1 ≤ q ≤ 5,000,1 ≤ p ≤ n,1 ≤ s ≤ 1,000,000,1 ≤ l ≤ r ≤ n。
都是整数
输出
第一行输出最温暖位置的温暖值和编号,用一个空格隔开。
接下来q行每行输出一个询问的答案。
只写数字,不加逗号
样例 1 · stdin
7 2 2
2 3
6 2
1 7
3 5
stdout
3 2
12
4
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

每堆篝火把能照到的位置逐个相加要m × n = 9千万次,对Python和Craykon太慢。
把前缀和用三层。
一堆篝火的温暖是一座山:从左边每格+1上升,过了p每格−1下降,所以“斜率”在区间p−s+1 … p是+1,在p+1 … p+s是−1。
对斜率做区间加只要改差分数组两端的两格(P0259的方法),全部收集后累加一次得到斜率,再累加一次得到温暖,再累加一次得到前缀和,一次减法就能求区间和。
例子:温暖是2 3 2 1 1 2 1,3到5号的和是2 + 1 + 1 = 4。
陷阱一:上坡的起点p − s + 1可能小于1。
Python的dd[-3]不报错,改的是倒数第三格;JavaScript、TypeScript会悄悄给数组加一个叫"-3"的属性;C、C++会碰到数组外的内存;Java、C#、Craykon会以运行时错误停下。
陷阱二:只把起点拉到1号也是错的 — 1号位置已经升到s − (p − 1),要把这个高度另外加到1号的斜率上。
向右越界的下坡只要丢掉n号之后的部分。
陷阱三:只累加一次就成了“平的区间加”,不是山形。
陷阱四:区间和是prefix[r] − prefix[l − 1],减prefix[l]会漏掉l号。
陷阱五:最温暖的位置用>=替换会留下编号大的。
陷阱六:一个位置的温暖到10^10,和约到9×10^13,C、C++用long long,Java、C#用long(32位int只到约21亿)。
Python整数不会溢出,JavaScript、TypeScript、Craykon的数在2^53(约9×10^15)以内是精确的。
Craykon用新列表(n + 3, 0)建一个全是0的列表,用数学.较大值和数学.较小值截取范围(Craykon的列表最多10,000项)。

Pythonmain.py · 44 行
import sys data = sys.stdin.buffer.read().split()n, m, q = int(data[0]), int(data[1]), int(data[2]) # dd[x]: x번 자리에서 '기울기'가 얼마나 바뀌는지(두 번 누적하면 따뜻함이 된다)dd = [0] * (n + 3)pos = 3for _ in range(m):    p, s = int(data[pos]), int(data[pos + 1])    pos += 2    # 왼쪽 비탈이 1번 자리 밖에서 시작하면, 1번 자리는 이미 s - (p - 1)만큼 따뜻하다    first = s - (p - 1)    if first > 0:        dd[1] += first        dd[2] -= first    lo = max(2, p - s + 1)      # 오르막: lo..p에서 한 칸에 +1    if lo <= p:        dd[lo] += 1        dd[p + 1] -= 1    hi = min(n, p + s)          # 내리막: p+1..hi에서 한 칸에 -1    if p + 1 <= hi:        dd[p + 1] -= 1        dd[hi + 1] += 1 prefix = [0] * (n + 1)          # prefix[x] = 1..x번 자리의 따뜻함의 합slope = 0warm = 0best = -1best_at = 0for x in range(1, n + 1):    slope += dd[x]              # 첫 번째 누적: 기울기    warm += slope               # 두 번째 누적: 따뜻함    prefix[x] = prefix[x - 1] + warm   # 세 번째 누적: 구간 합을 위한 누적 합    if warm > best:        best = warm        best_at = x out = [f"{best} {best_at}"]for _ in range(q):    l, r = int(data[pos]), int(data[pos + 1])    pos += 2    out.append(str(prefix[r] - prefix[l - 1]))print("\n".join(out))
JavaScriptmain.js · 49 行
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [n, m, q] = data; // dd[x]: x번 자리에서 '기울기'가 얼마나 바뀌는지(두 번 누적하면 따뜻함이 된다)const dd = new Array(n + 3).fill(0);let pos = 3;for (let i = 0; i < m; i++) {  const p = data[pos++];  const s = data[pos++];  // 왼쪽 비탈이 1번 자리 밖에서 시작하면, 1번 자리는 이미 s - (p - 1)만큼 따뜻하다  const first = s - (p - 1);  if (first > 0) {    dd[1] += first;    dd[2] -= first;  }  const lo = Math.max(2, p - s + 1); // 오르막: lo..p에서 한 칸에 +1  if (lo <= p) {    dd[lo] += 1;    dd[p + 1] -= 1;  }  const hi = Math.min(n, p + s); // 내리막: p+1..hi에서 한 칸에 -1  if (p + 1 <= hi) {    dd[p + 1] -= 1;    dd[hi + 1] += 1;  }} const prefix = new Array(n + 1).fill(0); // prefix[x] = 1..x번 자리의 따뜻함의 합let slope = 0;let warm = 0;let best = -1;let bestAt = 0;for (let x = 1; x <= n; x++) {  slope += dd[x];  warm += slope;  prefix[x] = prefix[x - 1] + warm;  if (warm > best) {    best = warm;    bestAt = x;  }} const out = [best + " " + bestAt];for (let i = 0; i < q; i++) {  const l = data[pos++];  const r = data[pos++];  out.push(prefix[r] - prefix[l - 1]);}console.log(out.join("\n"));
TypeScriptmain.ts · 51 行
import { readFileSync } from "node:fs"; const data: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [n, m, q] = data; // dd[x]: x번 자리에서 '기울기'가 얼마나 바뀌는지(두 번 누적하면 따뜻함이 된다)const dd: number[] = new Array<number>(n + 3).fill(0);let pos = 3;for (let i = 0; i < m; i++) {  const p: number = data[pos++];  const s: number = data[pos++];  // 왼쪽 비탈이 1번 자리 밖에서 시작하면, 1번 자리는 이미 s - (p - 1)만큼 따뜻하다  const first: number = s - (p - 1);  if (first > 0) {    dd[1] += first;    dd[2] -= first;  }  const lo: number = Math.max(2, p - s + 1); // 오르막: lo..p에서 한 칸에 +1  if (lo <= p) {    dd[lo] += 1;    dd[p + 1] -= 1;  }  const hi: number = Math.min(n, p + s); // 내리막: p+1..hi에서 한 칸에 -1  if (p + 1 <= hi) {    dd[p + 1] -= 1;    dd[hi + 1] += 1;  }} const prefix: number[] = new Array<number>(n + 1).fill(0); // prefix[x] = 1..x번 자리의 따뜻함의 합let slope = 0;let warm = 0;let best = -1;let bestAt = 0;for (let x = 1; x <= n; x++) {  slope += dd[x];  warm += slope;  prefix[x] = prefix[x - 1] + warm;  if (warm > best) {    best = warm;    bestAt = x;  }} const out: string[] = [`${best} ${bestAt}`];for (let i = 0; i < q; i++) {  const l: number = data[pos++];  const r: number = data[pos++];  out.push(String(prefix[r] - prefix[l - 1]));}console.log(out.join("\n"));
JavaMain.java · 55 行
import java.io.*; public class Main {    public static void main(String[] args) throws IOException {        StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));        st.nextToken(); int n = (int) st.nval;        st.nextToken(); int m = (int) st.nval;        st.nextToken(); int q = (int) st.nval;         // dd[x]: x번 자리에서 '기울기'가 얼마나 바뀌는지(두 번 누적하면 따뜻함이 된다)        long[] dd = new long[n + 3];        for (int i = 0; i < m; i++) {            st.nextToken(); int p = (int) st.nval;            st.nextToken(); int s = (int) st.nval;            // 왼쪽 비탈이 1번 자리 밖에서 시작하면, 1번 자리는 이미 s - (p - 1)만큼 따뜻하다            int first = s - (p - 1);            if (first > 0) {                dd[1] += first;                dd[2] -= first;            }            int lo = Math.max(2, p - s + 1); // 오르막: lo..p에서 한 칸에 +1            if (lo <= p) {                dd[lo] += 1;                dd[p + 1] -= 1;            }            int hi = Math.min(n, p + s); // 내리막: p+1..hi에서 한 칸에 -1            if (p + 1 <= hi) {                dd[p + 1] -= 1;                dd[hi + 1] += 1;            }        }         long[] prefix = new long[n + 1]; // 10^14까지 가서 int로는 넘친다        long slope = 0, warm = 0, best = -1;        int bestAt = 0;        for (int x = 1; x <= n; x++) {            slope += dd[x];            warm += slope;            prefix[x] = prefix[x - 1] + warm;            if (warm > best) {                best = warm;                bestAt = x;            }        }         StringBuilder out = new StringBuilder();        out.append(best).append(' ').append(bestAt).append('\n');        for (int i = 0; i < q; i++) {            st.nextToken(); int l = (int) st.nval;            st.nextToken(); int r = (int) st.nval;            out.append(prefix[r] - prefix[l - 1]).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 64 行
using System;using System.Text; class Program{    static void Main()    {        var data = Console.In.ReadToEnd().Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);        int n = int.Parse(data[0]), m = int.Parse(data[1]), q = int.Parse(data[2]);         // dd[x]: x번 자리에서 '기울기'가 얼마나 바뀌는지(두 번 누적하면 따뜻함이 된다)        var dd = new long[n + 3];        int pos = 3;        for (int i = 0; i < m; i++)        {            int p = int.Parse(data[pos++]);            int s = int.Parse(data[pos++]);            // 왼쪽 비탈이 1번 자리 밖에서 시작하면, 1번 자리는 이미 s - (p - 1)만큼 따뜻하다            int first = s - (p - 1);            if (first > 0)            {                dd[1] += first;                dd[2] -= first;            }            int lo = Math.Max(2, p - s + 1); // 오르막: lo..p에서 한 칸에 +1            if (lo <= p)            {                dd[lo] += 1;                dd[p + 1] -= 1;            }            int hi = Math.Min(n, p + s); // 내리막: p+1..hi에서 한 칸에 -1            if (p + 1 <= hi)            {                dd[p + 1] -= 1;                dd[hi + 1] += 1;            }        }         var prefix = new long[n + 1]; // 10^14까지 가서 int로는 넘친다        long slope = 0, warm = 0, best = -1;        int bestAt = 0;        for (int x = 1; x <= n; x++)        {            slope += dd[x];            warm += slope;            prefix[x] = prefix[x - 1] + warm;            if (warm > best)            {                best = warm;                bestAt = x;            }        }         var sb = new StringBuilder();        sb.Append(best).Append(' ').Append(bestAt).Append('\n');        for (int i = 0; i < q; i++)        {            int l = int.Parse(data[pos++]);            int r = int.Parse(data[pos++]);            sb.Append(prefix[r] - prefix[l - 1]).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 55 行
#include <algorithm>#include <iostream>#include <vector>using namespace std; int main() {    ios::sync_with_stdio(false);    cin.tie(nullptr);    int n, m, q;    cin >> n >> m >> q;     // dd[x]: x번 자리에서 '기울기'가 얼마나 바뀌는지(두 번 누적하면 따뜻함이 된다)    vector<long long> dd(n + 3, 0);    for (int i = 0; i < m; i++) {        int p, s;        cin >> p >> s;        // 왼쪽 비탈이 1번 자리 밖에서 시작하면, 1번 자리는 이미 s - (p - 1)만큼 따뜻하다        int first = s - (p - 1);        if (first > 0) {            dd[1] += first;            dd[2] -= first;        }        int lo = max(2, p - s + 1); // 오르막: lo..p에서 한 칸에 +1        if (lo <= p) {            dd[lo] += 1;            dd[p + 1] -= 1;        }        int hi = min(n, p + s); // 내리막: p+1..hi에서 한 칸에 -1        if (p + 1 <= hi) {            dd[p + 1] -= 1;            dd[hi + 1] += 1;        }    }     vector<long long> prefix(n + 1, 0); // 10^14까지 가서 int로는 넘친다    long long slope = 0, warm = 0, best = -1;    int bestAt = 0;    for (int x = 1; x <= n; x++) {        slope += dd[x];        warm += slope;        prefix[x] = prefix[x - 1] + warm;        if (warm > best) {            best = warm;            bestAt = x;        }    }     cout << best << ' ' << bestAt << '\n';    for (int i = 0; i < q; i++) {        int l, r;        cin >> l >> r;        cout << prefix[r] - prefix[l - 1] << '\n';    }    return 0;}
Cmain.c · 51 行
#include <stdio.h> #define MAXN 9000 static long long dd[MAXN + 3];     /* dd[x]: x번 자리에서 '기울기'가 얼마나 바뀌는지 */static long long prefix[MAXN + 1]; /* prefix[x] = 1..x번 자리의 따뜻함의 합(10^14까지) */ int main(void) {    int n, m, q;    if (scanf("%d %d %d", &n, &m, &q) != 3) return 1;    for (int i = 0; i < m; i++) {        int p, s;        scanf("%d %d", &p, &s);        /* 왼쪽 비탈이 1번 자리 밖에서 시작하면, 1번 자리는 이미 s - (p - 1)만큼 따뜻하다 */        int first = s - (p - 1);        if (first > 0) {            dd[1] += first;            dd[2] -= first;        }        int lo = p - s + 1 > 2 ? p - s + 1 : 2; /* 오르막: lo..p에서 한 칸에 +1 */        if (lo <= p) {            dd[lo] += 1;            dd[p + 1] -= 1;        }        int hi = p + s < n ? p + s : n; /* 내리막: p+1..hi에서 한 칸에 -1 */        if (p + 1 <= hi) {            dd[p + 1] -= 1;            dd[hi + 1] += 1;        }    }     long long slope = 0, warm = 0, best = -1;    int bestAt = 0;    for (int x = 1; x <= n; x++) {        slope += dd[x];        warm += slope;        prefix[x] = prefix[x - 1] + warm;        if (warm > best) {            best = warm;            bestAt = x;        }    }     printf("%lld %d\n", best, bestAt);    for (int i = 0; i < q; i++) {        int l, r;        scanf("%d %d", &l, &r);        printf("%lld\n", prefix[r] - prefix[l - 1]);    }    return 0;}
Craykonmain.cray · 56 行
첫줄 = 控制台.读数字();n = 첫줄[0];m = 첫줄[1];q = 첫줄[2]; // dd[x]: x번 자리에서 '기울기'가 얼마나 바뀌는지(두 번 누적하면 따뜻함이 된다)dd = 新列表(n + 3, 0);循环 (i = 0; i < m; i++){    줄 = 控制台.读数字();    p = 줄[0];    s = 줄[1];    // 왼쪽 비탈이 1번 자리 밖에서 시작하면, 1번 자리는 이미 s - (p - 1)만큼 따뜻하다    first = s - (p - 1);    如果 (first > 0)    {        dd[1] += first;        dd[2] -= first;    }    lo = 数学.较大值(2, p - s + 1); // 오르막: lo..p에서 한 칸에 +1    如果 (lo <= p)    {        dd[lo] += 1;        dd[p + 1] -= 1;    }    hi = 数学.较小值(n, p + s); // 내리막: p+1..hi에서 한 칸에 -1    如果 (p + 1 <= hi)    {        dd[p + 1] -= 1;        dd[hi + 1] += 1;    }} prefix = 新列表(n + 1, 0); // prefix[x] = 1..x번 자리의 따뜻함의 합slope = 0;warm = 0;best = -1;bestAt = 0;循环 (x = 1; x <= n; x++){    slope += dd[x];    warm += slope;    prefix[x] = prefix[x - 1] + warm;    如果 (warm > best)    {        best = warm;        bestAt = x;    }} 控制台.输出(best + " " + bestAt);循环 (i = 0; i < q; i++){    줄 = 控制台.读数字();    控制台.输出(prefix[줄[1]] - prefix[줄[0] - 1]);}