0
点赞
收藏
分享

微信扫一扫

二阶差分(注意数据范围)

夏木之下 2022-03-22 阅读 75

题目描述

小明在练习绝世武功, nn 个练功桩排成一排,一开始每个桩的损伤为 00。

接下来小明会练习 mm 种绝世武功,每种武功都会对 [l, r][l,r] 区间分别造成 [s,e][s,e] 的伤害。

这个伤害是一个等差序列。例如 l = 1, r = 4, s = 2, e = 8l=1,r=4,s=2,e=8 ,则会对 1-41−4 号练功桩造成2, 4, 6, 82,4,6,8 点损伤。

小明想让你统计一下所有练功桩的损伤的和。

输入描述

第一行输入 n, mn,m,代表练功桩的数量和绝世武功的种类数。

接下来 mm 行输入 44 个整数 l, r, s, el,r,s,e 。

1 \leq n \leq 10^7 , 1\leq m \leq 3 \times 10 ^ 5 , 1\leq l, r \leq n1≤n≤107,1≤m≤3×105,1≤l,r≤n

输出描述

输出一个整数代表所有练功桩的损伤和, 题目保证所有输入输出都在 [0, 9 \times 10^{18}][0,9×1018]

输入输出样例

示例 1

6 2
1 5 2 10
2 4 1 1

33

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 512M

原题链接:https://www.lanqiao.cn/problems/1368/learning/

 

做法一:朴素做法:

#include <iostream>
using namespace std;

const long long int N = 10000010, M =300010;
int a[N], q[N];

int main()
{
  long long int l, r, s, e;
  long long int n, m;
  scanf("%lld %lld",&n,&m);

  long long int res = 0;
  while(m --)
  {
    scanf("%lld %lld %lld %lld", &l, &r, &s, &e);
    res += (s + e) * (r - l + 1) / 2;
  }
  cout << res << endl;

  return 0;
}

做法二:二阶差分:

#include<iostream>

#define ll long long

using namespace std;

const int N = 1e7 + 10;
ll c[N];

int main()
{
    int n, m; cin >> n >> m;

      while (m--)
     {
      int l, r, s, e;
      cin >> l >> r >> s >> e;
      int d = (e - s) / (r - l);
      c[l] += s;
      c[l + 1] += d - s;
      c[r + 1] -= d + e;
      c[r + 2] += e;
     }

      for (int i = 1; i <= n; i++)
     {
      c[i] += c[i - 1];
     }

      ll sum = 0;
      for (int i = 1; i <= n; i++)
     {
          c[i] += c[i - 1];
          sum += c[i];
     }

     cout << sum << endl;
     return 0;
}

二阶差分的公式:

 

 

举报

相关推荐

0 条评论