问题引入

  Nephry 最近迷上了天体物理学看星星,这是她一周观察火星运行之后记录的数据(纯属虚构):

Mon. Tue. Wed. Thu. Fri.
太阳-火星距离 \(3\times10^4 km\) \(6\times10^4km\) 刷题去了 \(5\times10^4km\) \(7\times10^4km\)

  现在 Nephry 想通过其它四组数据来推测周三的数据。

思考

  显然应该抽象成函数关系。那么我们可以建立 “Dist-Date” 图如下:

  其中绿色的轨迹:\(f(x)=\frac{1}2x^3-\frac{14}3x^2+\frac{27}2x-\frac{19}3\) 就是一种满足已知数据的函数关系,它也是唯一过这四个点的三次函数。当然,实际物理问题中的函数关系远远不止三次这么简单,我们只是找到其中一种情况,并期望借此来估测 \(f(3)\) 的值。

Lagrange 插值法

  题外话,Lagrange——

  (大雾。

  为解决诸如此类问题,法国数学家 Lagrange 提出了一种构造恰好穿过平面上若干已知点的多项式函数的方法,也即是 Lagrange 插值法。所谓插值,度娘解释道:

插值是数学领域数值分析中的通过已知的离散数据求未知数据的过程或方法。

  通俗地理解成,“猜出”系列离散数据的连续函数关系,并借此求出未知数据的方法。

插值过程

  举一个最简单的例子,已知一个二次函数 \(f(x)\) 过三点 \((x_1,y_1),(x_2,y_2),(x_3,y_3)\),求 \(f(x)\) 的解析式。

  代入解方程?不!上面那个函数把兔兔算傻了qwq。 Lagrange 通过构造三条二次曲线相加的方法来解决这个问题:

  第一条曲线 \(f_1(x)\),满足 \(f_1(x_1)=1,f_1(x_2)=f_1(x_3)=0\),如图(懒得画w,搬的课件):

  第二条曲线 \(f_2(x)\),满足 \(f_2(x_2)=1,f_2(x_1)=f_2(x_3)=0\),如图:

  类似地,第三条曲线 \(f_3(x)\) 应有 \(f_3(x_3)=1,f_3(x_1)=f_3(x_2)=0\),如图:

  趁你不明所以,Lagrange 写下一个等式:

\[f(x)=y_1f_1(x)+y_2f_2(x)+y_3f_3(x)
\]

  代入 \(x_1,x_2,x_3\),发现 \(f(x)\) 正满足恰好过三个点的条件!(原理很简单,不赘述w。)

  推而广之,并加以严谨的数学语言描述,假设现有 \(n\) 个点 \((x_1,y_1),(x_2,y_2),\dots,(x_n,y_n)\),要找到恰好穿过这 \(n\) 个点的 \(n-1\) 次函数 \(f(x)\),我们只需要构造 \(n\) 条 \(n-1\) 次曲线。其中第 \(i\) 条有:

\[f_i(x)=\begin{cases}1&x=x_i\\0&x\in\{x_1,x_2,\dots,x_n\}-\{x_i\}\end{cases}
\]

  如何构造出 \(i\) 呢?利用小学二年级的因式分解知识,我们知道 \(f_i(x)\) 必然含有因式 \((x-t),~t\in\{x_1,x_2,\dots,x_n\}-\{x_i\}\) 。接下来只需要使得 \(f_i(x_i)=1\),即所有以上因式都被约掉。那么就有:

\[f_i(x)=\prod_{j\in[1,n]-\{i\}}\frac{x-x_j}{x_i-x_j}
\]

  最后,就可以根据这 \(n\) 条曲线构造出:

\[f(x)=\sum_{i=1}^ny_if_i(x)
\]

代码实现

  功能:读入 \(n\) 个点 \((x_i,y_i)\),求出过这 \(n\) 个点的曲线在 \(x=k\) 处的取值,对大素数取模。

  1. inline int lagrange ( const vector<int> x, const vector<int> y, const int k ) {
  2. int n = x.size (), ret = 0;
  3. for ( int i = 0; i < n; ++ i ) {
  4. int num = y[i], den = 1;
  5. for ( int j = 0; j < n; ++ j ) {
  6. if ( i ^ j ) {
  7. num = 1ll * num * ( k - x[j] + p ) % p; // 直接代入 x=k 运算.
  8. den = 1ll * den * ( x[i] - x[j] + p ) % p;
  9. }
  10. }
  11. ret = ( ret + 1ll * num * qkpow ( den, p - 2 ) ) % p; // 外层 Sigma 求和.
  12. }
  13. return ret;
  14. }

  复杂度 \(O(n^2)\)。

实际应用

  注意下文所述的 “\(f(x)\) 是关于 \(x\) 的 \(n\) 次函数” 均指其最高次不超过 \(n\) 次

「洛谷 P4781」「模板」拉格朗日插值

  link.

  如上文的代码实现,不赘述。

「洛谷 P4463」calc

题意简述

  link.

  给定 \(k,n,p\),求:

\[\sum_{\{a_n\}}\left[(\forall i)\left(a_i\in[1,k]\right)\right]\left[(\forall i\not=j)\left(a_i\not=a_j\right)\right]\prod_{i=1}^n a_i\bmod p
\]

数据规模

  \(n\le500;~n+1<k<p\le10^9\)。

Solution

Step 1

  有一个比较 naive 的 DP:令 \(f(i,j)\) 表示前 \(i\) 个元素取值在 \([1,j]\) 之间,构成的单增序列的值(即\(\prod_{t=1}^ia_t\))之和。易有:

\[f(i,j)=j\cdot f(i-1,j-1)+f(i,j-1)
\]

  那么答案为:

\[ans=n!f(n,k)
\]

  复杂度 \(O(nk)\),不可过。

Step 2

  接下来,结合本文的主题,我们将会证明,当 \(n\) 为常数,\(f(n,x)\) 是关于 \(x\) 的 \(2n+1\) 次的函数

证明

  差分 \(f(i)\),令 \(g(i,j)=f(i,j)-f(i,j-1)\)。结合原题,有转移:

\[g(i,j)=j\sum_{t=0}^{j-1}g(i-1,t)
\]

  同时,有:

\[f(i,j)=\sum_{t=0}^jg(i,j)
\]

  由于对函数 \(f(i)\) 差分相当于将其次数 \(-1\)(最高次展开后抵消)。故只需证 \(g(n,x)\) 是关于 \(x\) 的 \(2n\) 次函数。

  考虑归纳:

  • \(1).\) 当 \(n=0\) 时,\(g(n,x)=[x=0]\),成立。

  • \(2).\) 当 \(n=m\) 时成立,考察 \(n=m+1\) 的情况:

    \[g(m+1,x)=x\sum_{t=0}^{x-1}g(m,t)
    \]

      求前缀和,次数 \(+1\),再带上系数 \(x\),次数总共 \(+2\)。由于 \(n=m\) 是次数为 \(2m\),故 \(n=m+1\) 时次数为 \(2m+2=2(m+1)\)。

      由 \(1).~2).\) \(g(n,x)\) 是关于 \(x\) 的 \(2n\) 次函数得证,故有 \(f(n,x)\) 是关于 \(x\) 的 \(2n+1\) 次函数,证毕。

  由此,对于 \(k\le2n+1\) 的情况,我们直接 DP 得出答案;否则只需要 DP 出 \(f(n,1..2n+1)\) 的值,然后利用 Lagrange 插值法求出已被形如 \(\left(x,f(n,x)\right)\) 的 \(2n+1\) 个点唯一确定的 \(2n+1\) 次函数,最后代入 \(x=k\) 求得答案即可。

  复杂度 \(O(n^2)\)。

代码

  1. #include <cstdio>
  2. const int MAXN = 500;
  3. int n, k, p, x[MAXN + 5], f[MAXN + 5][MAXN * 2 + 5];
  4. inline int qkpow ( int a, int b ) {
  5. int ret = 1;
  6. for ( ; b; a = 1ll * a * a % p, b >>= 1 ) ret = 1ll * ret * ( b & 1 ? a : 1 ) % p;
  7. return ret;
  8. }
  9. inline int lagrange ( const int n, const int* x, const int* y, const int k ) {
  10. int ret = 0;
  11. for ( int i = 1; i <= n; ++ i ) {
  12. int num = y[i], den = 1;
  13. for ( int j = 1; j <= n; ++ j ) {
  14. if ( i ^ j ) {
  15. num = 1ll * num * ( k - x[j] + p ) % p;
  16. den = 1ll * den * ( x[i] - x[j] + p ) % p;
  17. }
  18. }
  19. ret = ( ret + 1ll * num * qkpow ( den, p - 2 ) ) % p;
  20. }
  21. return ret;
  22. }
  23. int main () {
  24. scanf ( "%d %d %d", &k, &n, &p );
  25. int fac = 1;
  26. for ( int i = 0; i <= k && i <= ( n << 1 | 1 ); ++ i ) f[0][i] = 1;
  27. for ( int i = 1; i <= n; fac = 1ll * fac * i ++ % p ) {
  28. for ( int j = 1; j <= k && j <= ( n << 1 | 1 ); ++ j ) {
  29. f[i][j] = ( 1ll * j * f[i - 1][j - 1] + f[i][j - 1] ) % p;
  30. }
  31. }
  32. if ( k <= ( n << 1 | 1 ) ) return printf ( "%d\n", int ( 1ll * fac * f[n][k] % p ) ), 0;
  33. for ( int i = 1; i <= ( n << 1 | 1 ); ++ i ) x[i] = i;
  34. return printf ( "%d\n", int ( 1ll * fac * lagrange ( n << 1 | 1, x, f[n], k ) % p ) ), 0;
  35. }

「CF 995F」Cowmpany Cowmpensation

题意简述

  link.

  给定一棵 \(n\) 个结点的有根树,用 \([1,d]\) 为每个结点染色,要求子结点颜色不大于父节点颜色,求方案数。对大素数取模。

数据规模

  \(n\le3000;~d\le10^9\)。

Solution

Step 1

  按照上一题的套路,我们还是先列出一个暴力的 DP。令 \(f(u,i)\) 表示以 \(u\) 为根的子树用 \([1,i]\) 染色的方案数,转移显然有:

\[f(u,i)=f(u,i-1)+\prod_{v\in son_u}f(v,i)
\]

  直接 DP,复杂度 \(O(nd)\),不可过。

Step 2

  我们发现这个式子和上一题的其实有些类似,所以……

  大声说出你的猜想!

  Bingo!设 \(u\) 的子树大小为 \(s_u\),则 \(f(u,x)\) 是关于 \(x\) 的 \(s_u\) 次函数

证明

  仍考虑归纳:

  • \(1).\) 当 \(u\) 为叶子结点时,\(f(u,x)=1\),成立。

  • \(2).\) 当 \(v\in son_u\) 时成立,考虑 \(u\):

    \[f(u,x)-f(u,x-1)=\prod_{v\in son_u}f(v,x)
    \]

      由于 \(v\) 满足结论,即 \(f(v,x)\) 的次数为 \(s_v\),则 \(f(u,x)-f(u,x-1)\) 的次数为 \(\sum_{v\in son_u}s_v\),即 \(s_u-1\)。再还原差分,次数 \(+1\),有 \(f(u,x)\) 是关于 \(x\) 的 \(s_u-1+1=s_u\) 次函数。

      由 \(1).~2).\) 证毕。

  所以,只需要处理出 \(f(u,1..n)\),最后利用 \(\left(x,f(1,x)\right)~(x\in[0,n])\) 求出 \(f(1,x)\) 的曲线方程,再代入 \(d\) 即可。

  连续插值部分可以优化到 \(O(n)\),不过这并不是唯一的瓶颈复杂度。最终复杂度仍是 \(O(n^2)\)。

代码

  1. #include <cstdio>
  2. const int MAXN = 3000, MOD = 1e9 + 7;
  3. int n, d, ecnt, head[MAXN + 5], x[MAXN + 5], f[MAXN + 5][MAXN + 5];
  4. struct Edge { int to, nxt; } graph[MAXN + 5];
  5. inline int qkpow ( int a, int b, const int p = MOD ) {
  6. int ret = 1;
  7. for ( ; b; a = 1ll * a * a % p, b >>= 1 ) ret = 1ll * ret * ( b & 1 ? a : 1 ) % p;
  8. return ret;
  9. }
  10. inline int lagrange ( const int n, const int* x, const int* y, const int k ) {
  11. int ret = 0;
  12. for ( int i = 1; i <= n; ++ i ) {
  13. int num = y[i], den = 1;
  14. for ( int j = 1; j <= n; ++ j ) {
  15. if ( i ^ j ) {
  16. num = 1ll * num * ( k - x[j] + MOD ) % MOD;
  17. den = 1ll * den * ( x[i] - x[j] + MOD ) % MOD;
  18. }
  19. }
  20. ret = ( ret + 1ll * num * qkpow ( den, MOD - 2 ) ) % MOD;
  21. }
  22. return ret;
  23. }
  24. inline void link ( const int s, const int t ) { graph[++ ecnt] = { t, head[s] }, head[s] = ecnt; }
  25. inline void DFS ( const int u ) {
  26. for ( int i = 1; i <= n; ++ i ) f[u][i] = 1;
  27. for ( int i = head[u], v; i; i = graph[i].nxt ) {
  28. DFS ( v = graph[i].to );
  29. for ( int j = 1; j <= n; ++ j ) f[u][j] = 1ll * f[u][j] * f[v][j] % MOD;
  30. }
  31. for ( int i = 2; i <= n; ++ i ) f[u][i] = ( f[u][i] + f[u][i - 1] ) % MOD;
  32. }
  33. int main () {
  34. scanf ( "%d %d", &n, &d );
  35. for ( int i = 1; i <= n; ++ i ) x[i] = i;
  36. for ( int i = 2, f; i <= n; ++ i ) scanf ( "%d", &f ), link ( f, i );
  37. printf ( "%d\n", ( DFS ( 1 ), d <= n ? f[1][d] : lagrange ( n + 1, x - 1, f[1] - 1, d ) ) );
  38. return 0;
  39. }

「CF 662F」The Sum of the k-th Powers

题意简述

  link.

  给定 \(n,k\),求 \(\sum_{i=1}^ni^k\bmod(10^9+7)\)。

数据规模

  \(n\le10^9;~k\le10^6\)。

Solution

  见下文结论 1。(谋篇布局的大失败qwq)暴力求出前 \(O(k)\) 个值,插值即可。

  复杂度 \(O(k\log(10^9+7))\)。

代码

  1. #include <cstdio>
  2. #define inv( x ) qkpow ( x, MOD - 2 )
  3. const int MAXN = 1e6, MOD = 1e9 + 7;
  4. int n, k, y[MAXN + 5], ifac[MAXN + 5];
  5. inline int qkpow ( int a, int b, const int p = MOD ) {
  6. int ret = 1;
  7. for ( ; b; a = 1ll * a * a % p, b >>= 1 ) ret = 1ll * ret * ( b & 1 ? a : 1 ) % p;
  8. return ret;
  9. }
  10. int main () {
  11. scanf ( "%d %d", &n, &k ), ifac[0] = 1;
  12. for ( int i = 1; i <= k + 1; ++ i ) ifac[i] = 1ll * ifac[i - 1] * inv ( i ) % MOD;
  13. for ( int i = 1; i <= k + 1; ++ i ) y[i] = ( y[i - 1] + qkpow ( i, k ) ) % MOD;
  14. if ( n <= k + 1 ) return printf ( "%d\n", y[n] ), 0;
  15. int all = 1, ans = 0;
  16. for ( int i = 0; i <= k + 1; ++ i ) all = 1ll * all * ( n - i + MOD ) % MOD;
  17. for ( int i = 0; i <= k + 1; ++ i ) {
  18. ans = ( ans + ( ( k + 1 - i ) & 1 ? ( MOD - 1ll ) : 1ll )
  19. * y[i] % MOD * all % MOD * inv ( ( n - i + MOD ) % MOD ) % MOD
  20. * ifac[i] % MOD * ifac[k + 1 - i] % MOD ) % MOD;
  21. }
  22. return printf ( "%d\n", ans ), 0;
  23. }

「BZOJ 3453」tyvj 1858 XLkxc

题意简述

  给定 \(k,a,n,d\),求:

\[\sum_{i=0}^n\sum_{j=1}^{a+id}\sum_{x=1}^jx^k\bmod1234567891
\]

数据规模

  \(k\le123;~a,n,d\le123456789\)。

  什么鬼畜的范围哟……

Solution

  别问,问就是信仰插值。

  算法本身很显,我们先来证明一个对于分析函数次数比较有用的结论吧。

结论 1

  前 \(n\) 个正整数的 \(k\) 次幂和是 \(k+1\) 次多项式。

  证明可参考这篇博客

结论 2

  设 \(f(x),g(x)\) 是关于 \(x\) 的多项式函数,\(f(x)\) 的次数为 \(p_f\),\(g(x)\) 的次数为 \(p_g\),对于函数:

\[h(x)=\sum_{i=0}^xf(g(i))
\]

  其中 \(x\in\mathbb N\),则 \(h(x)\) 的次数 \(p_h=p_f+p_g+1\)。

证明

  既然只考虑最高次,不妨 \(f(x)=x^{p_f},g(x)=x^{p_g}\),代入有 \(h(x)=\sum_{i=0}^xi^{p_f+p_g}\),利用结论 1 即可证明。

本题

  记 \(f(x)=\sum_{x=1}^jx^k\),\(g(x)=\sum_{j=1}^{a+id}\sum_{x=1}^jx^k=\sum_{j=1}^{a+id}f(j)\),\(h(x)=\sum_{i=0}^n\sum_{j=1}^{a+id}\sum_{x=1}^jx^k=\sum_{i=0}^ng(a+id)\)。由结论 2,\(f(x),g(x),h(x)\) 的次数分别为 \(k+1,k+2,k+3\),直接插值即可。

  复杂度 \(O(Tk^2)\)。

代码

  1. #include <cstdio>
  2. #define inv( x ) qkpow ( ( x ) % MOD, MOD - 2 )
  3. typedef long long LL;
  4. const LL MAXK = 123, MOD = 1234567891;
  5. LL k, a, n, d, ifac[MAXK + 5], s[MAXK + 5], t[MAXK + 5], x[MAXK + 5], y[MAXK + 5];
  6. inline LL qkpow ( LL a, LL b, const LL p = MOD ) {
  7. LL ret = 1;
  8. for ( ; b; a = a * a % p, b >>= 1 ) ret = ret * ( b & 1 ? a : 1 ) % p;
  9. return ret;
  10. }
  11. inline LL calc ( const LL k, const LL* y, const LL n ) {
  12. if ( n <= k ) return y[n];
  13. LL all = 1, ret = 0;
  14. for ( int i = 0; i <= k; ++ i ) all = all * ( n - i + MOD ) % MOD;
  15. for ( int i = 0; i <= k; ++ i ) {
  16. ret = ( ret + ( ( k - i ) & 1 ? ( MOD - 1 ) : 1 )
  17. * y[i] % MOD * all % MOD * inv ( ( n - i + MOD ) % MOD ) % MOD
  18. * ifac[i] % MOD * ifac[k - i] % MOD ) % MOD;
  19. }
  20. return ret;
  21. }
  22. int main () {
  23. ifac[0] = 1;
  24. for ( int i = 1; i <= MAXK + 3; ++ i ) ifac[i] = inv ( i ) * ifac[i - 1] % MOD;
  25. int T;
  26. for ( scanf ( "%d", &T ); T --; ) {
  27. scanf ( "%lld %lld %lld %lld", &k, &a, &n, &d );
  28. for ( int i = 1; i <= k + 2; ++ i ) s[i] = ( s[i - 1] + qkpow ( i, k ) ) % MOD;
  29. for ( int i = 1; i <= k + 2; ++ i ) t[i] = ( t[i - 1] + s[i] ) % MOD;
  30. y[0] = calc ( k + 2, t, a );
  31. for ( int i = 1; i <= k + 3; ++ i ) y[i] = ( y[i - 1] + calc ( k + 2, t, ( a + i * d % MOD ) % MOD ) ) % MOD;
  32. printf ( "%lld\n", calc ( k + 3, y, n ) );
  33. }
  34. return 0;
  35. }

「CF 1086F」Forest Fires

题意简述

  link.

  一个无限大的网格图上在第 \(0\) 时刻有 \(n\) 个格子着火,每一时刻,每个着火的格子会让周围八连通的格子着火。一个格子的权值定义为此格子最早的着火时刻,没有着火的格子权值为 \(0\)。求第 \(t\) 时刻所有格子的权值之和。

数据规模

  \(n\le50;~|x_i|,|y_i|,t\le10^8\)。

Solution

Step 0 前置-矩形面积并

  求网格图上多个可交矩形的覆盖面积和,用扫描线+线段树 \(O(n\log n)\) 求解。这里不赘述。

Step 1

  令 \(f(i)\) 表示 \(t\) 时刻时权值为 \(i\) 的格子的个数。那么答案就是 \(\sum_{i=0}^ti\cdot f(i)\) 如果强行求每一个 \(f(i)\) 是很复杂的,因为权值为 \(i\) 的格子的分布是许多个环。

  换种思路——对 \(f(i)\) 求前缀和,令 \(g(i)=\sum_{j=0}^if(i)\)。可以发现 \(g(i)\) 实际上就是 \(i\) 时刻网格图上被覆盖的面积之和。并且 \(g(t)-g(i)\) 实质上就是权值大于 \(i\) 的格子数量。所以答案又可以表示为 \(\sum_{i=0}^{t-1}g(t)-g(i)=tg(t)-\sum_{i=0}^{t-1}g(i)\)。

  如果暴力求每个 \(g(i)\),复杂度 \(O(tn\log n)\)。原地爆炸。

Step 2

  再次结合本文主题,我们来研究一下关于 \(x\) 的函数 \(g(x)\) 的次数性质。

  首先,假设矩形都不交。单独考虑每个矩形,它的面积按时刻递增呈现匀加速增长,这是典型的二次函数。所以,常数(指与 \(x\) 无关)个二次函数之和仍然是二次函数。

  接着,考虑两矩形相交后的情形。我们可以把这两个矩形整体看做一个不规则图形。单独考虑上下左右每一面,其随时刻增加,仍是匀加速增涨,面积亦为二次函数。

  综合上面两个结论,可以得到:当没有新的矩形相交时,\(g(x)\) 是关于 \(x\) 的二次函数。也即是,\(g(x)\) 是一个分段函数,每一段都是一个二次函数

  显然段数为 \(O(n^2)\),对于每一段,先暴力算出三个 \(g(x)\) 的值,以此解出 \(g(x)=ax^2+bx+c\)(可以插值,也可以手玩解方程),再求一个 \(g(x)\) 的前缀和即可。

  复杂度 \(O(n^3\log n)\)。

代码

  1. #include <cstdio>
  2. #include <vector>
  3. #include <algorithm>
  4. inline int rint () {
  5. int x = 0, f = 1; char s = getchar ();
  6. for ( ; s < '0' || '9' < s; s = getchar () ) f = s == '-' ? -f : f;
  7. for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
  8. return x * f;
  9. }
  10. template<typename Tp>
  11. inline void wint ( Tp x ) {
  12. if ( x < 0 ) putchar ( '-' ), x = ~ x + 1;
  13. if ( 9 < x ) wint ( x / 10 );
  14. putchar ( x % 10 ^ '0' );
  15. }
  16. inline int abs_ ( const int x ) { return x < 0 ? -x : x; }
  17. inline int max_ ( const int a, const int b ) { return a < b ? b : a; }
  18. const int MAXN = 50, MOD = 998244353, INV2 = 499122177, INV6 = 166374059;
  19. int n, t, x[MAXN + 5], y[MAXN + 5];
  20. namespace MatrixInter {
  21. int tmp[MAXN * 4 + 5];
  22. class SegmentTree {
  23. private:
  24. int cover[MAXN << 4], cnt[MAXN << 4];
  25. public:
  26. inline void clear ( const int rt, const int l, const int r ) {
  27. cover[rt] = cnt[rt] = 0;
  28. if ( l == r ) return ;
  29. int mid = l + r >> 1;
  30. clear ( rt << 1, l, mid ), clear ( rt << 1 | 1, mid + 1, r );
  31. }
  32. inline void pushup ( const int rt, const int l, const int r ) {
  33. cover[rt] = cnt[rt] ? tmp[r] - tmp[l - 1] : cover[rt << 1] + cover[rt << 1 | 1];
  34. }
  35. inline void add ( const int rt, const int al, const int ar, const int l, const int r, const int v ) {
  36. if ( al <= l && r <= ar ) {
  37. if ( ! ( cnt[rt] += v ) ) cover[rt] = l == r ? 0 : cover[rt << 1] + cover[rt << 1 | 1];
  38. else cover[rt] = tmp[r] - tmp[l - 1];
  39. return ;
  40. }
  41. int mid = l + r >> 1;
  42. if ( al <= mid ) add ( rt << 1, al, ar, l, mid, v );
  43. if ( mid < ar ) add ( rt << 1 | 1, al, ar, mid + 1, r, v );
  44. pushup ( rt, l, r );
  45. }
  46. inline int getTop () { return cover[1]; }
  47. } st;
  48. struct Event { int l, r, opt; };
  49. std :: vector<Event> evt[MAXN * 4 + 5];
  50. inline int calcS ( const int t ) {
  51. int cnt = 0;
  52. for ( int i = 1; i <= n; ++ i ) {
  53. tmp[++ cnt] = x[i] - t - 1, tmp[++ cnt] = x[i] + t;
  54. tmp[++ cnt] = y[i] - t - 1, tmp[++ cnt] = y[i] + t;
  55. }
  56. std :: sort ( tmp + 1, tmp + cnt + 1 );
  57. cnt = std :: unique ( tmp + 1, tmp + cnt + 1 ) - tmp - 1;
  58. st.clear ( 1, 1, cnt );
  59. for ( int i = 1; i <= cnt; ++ i ) evt[i].clear ();
  60. for ( int i = 1, up, dn, le, ri; i <= n; ++ i ) {
  61. up = std :: lower_bound ( tmp + 1, tmp + cnt + 1, y[i] - t - 1 ) - tmp + 1;
  62. dn = std :: lower_bound ( tmp + 1, tmp + cnt + 1, y[i] + t ) - tmp;
  63. le = std :: lower_bound ( tmp + 1, tmp + cnt + 1, x[i] - t - 1 ) - tmp;
  64. ri = std :: lower_bound ( tmp + 1, tmp + cnt + 1, x[i] + t ) - tmp;
  65. evt[le].push_back ( { up, dn, true } ), evt[ri].push_back ( { up, dn, false } );
  66. }
  67. int ret = 0;
  68. for ( int i = 1; i <= cnt; ++ i ) {
  69. ret = ( ret + 1ll * st.getTop () * ( tmp[i] - tmp[i - 1] ) % MOD ) % MOD;
  70. for ( Event e: evt[i] ) st.add ( 1, e.l, e.r, 1, cnt, e.opt ? 1 : -1 );
  71. }
  72. return ret;
  73. }
  74. } // namespace MatrixInter.
  75. int itime[MAXN * MAXN + 5];
  76. inline int preS ( const int n ) { return n * ( n + 1ll ) % MOD * INV2 % MOD; }
  77. inline int sqrS ( const int n ) { return n * ( n + 1ll ) % MOD * ( n * 2ll + 1 ) % MOD * INV6 % MOD; }
  78. int main () {
  79. #define MI MatrixInter
  80. n = rint (), t = rint ();
  81. for ( int i = 1; i <= n; ++ i ) x[i] = rint (), y[i] = rint ();
  82. int icnt = 0;
  83. for ( int i = 1; i < n; ++ i ) {
  84. for ( int j = i + 1, tmpt; j <= n; ++ j ) {
  85. tmpt = max_ ( abs_ ( x[i] - x[j] ), abs ( y[i] - y[j] ) ) >> 1;
  86. if ( tmpt <= t ) itime[++ icnt] = tmpt;
  87. if ( ++ tmpt <= t ) itime[++ icnt] = tmpt;
  88. }
  89. }
  90. itime[++ icnt] = 0, itime[++ icnt] = t;
  91. std :: sort ( itime + 1, itime + icnt + 1 );
  92. icnt = std :: unique ( itime + 1, itime + icnt + 1 ) - itime - 1;
  93. int ans = 1ll * t * MI :: calcS ( t ) % MOD;
  94. for ( int i = 1, tl, tr, a, b, c, ta, tb; i < icnt; ++ i ) {
  95. if ( ( tl = itime[i] ) + 3 > ( tr = itime[i + 1] ) ) {
  96. for ( ; tl < tr; ++ tl ) ans = ( ans - MI :: calcS ( tl ) + MOD ) % MOD;
  97. continue;
  98. }
  99. c = MI :: calcS ( tl ), ta = MI :: calcS ( tl + 1 ), tb = MI :: calcS ( tl + 2 );
  100. a = ( ( tb - ta * 2ll % MOD + c ) % MOD + MOD ) % MOD * INV2 % MOD;
  101. b = ( ( ta - c - a ) % MOD + MOD ) % MOD;
  102. ans = ( ans - 1ll * a * sqrS ( tr - tl - 1 ) % MOD + MOD ) % MOD;
  103. ans = ( ans - 1ll * b * preS ( tr - tl - 1 ) % MOD + MOD ) % MOD;
  104. ans = ( ans - 1ll * c * ( tr - tl ) % MOD + MOD ) % MOD;
  105. }
  106. return wint ( ans ), putchar ( '\n' ), 0;
  107. }

Note -「Lagrange 插值」学习笔记的更多相关文章

  1. Note -「动态 DP」学习笔记

    目录 「CF 750E」New Year and Old Subsequence 「洛谷 P4719」「模板」"动态 DP" & 动态树分治 「洛谷 P6021」洪水 「S ...

  2. Note -「单位根反演」学习笔记

    \(\mathcal{Preface}\)   单位根反演,顾名思义就是用单位根变换一类式子的形式.有关单位根的基本概念可见我的这篇博客. \(\mathcal{Formula}\)   单位根反演的 ...

  3. 「Manacher算法」学习笔记

    觉得这篇文章写得特别劲,插图非常便于理解. 目的:求字符串中的最长回文子串. 算法思想 考虑维护一个数组$r[i]$代表回文半径.回文半径的定义为:对于一个以$i$为回文中心的奇数回文子串,设其为闭区 ...

  4. 「FHQ Treap」学习笔记

    话说天下大事,就像fhq treap —— 分久必合,合久必分 简单讲一讲.非旋treap主要依靠分裂和合并来实现操作.(递归,不维护fa不维护cnt) 合并的前提是两棵树的权值满足一边的最大的比另一 ...

  5. 「线性基」学习笔记and乱口胡总结

    还以为是什么非常高大上的东西花了1h不到就学好了 线性基 线性基可以在\(O(nlogx)\)的时间内计算出\(n\)个数的最大异或和(不需要相邻). 上述中\(x\)表示的最大的数. 如何实现 定义 ...

  6. 「Link-Cut Tree」学习笔记

    Link-Cut Tree,用来解决动态树问题. 宏观上,LCT维护的是森林而非树.因此存在多颗LCT.有点像动态的树剖(链的确定通过$Access$操作),每条链用一颗$splay$维护.$spla ...

  7. 「AC自动机」学习笔记

    AC自动机(Aho-Corasick Automaton),虽然不能够帮你自动AC,但是真的还是非常神奇的一个数据结构.AC自动机用来处理多模式串匹配问题,可以看做是KMP(单模式串匹配问题)的升级版 ...

  8. 【Java】「深入理解Java虚拟机」学习笔记(1) - Java语言发展趋势

    0.前言 从这篇随笔开始记录Java虚拟机的内容,以前只是对Java的应用,聚焦的是业务,了解的只是语言层面,现在想深入学习一下. 对JVM的学习肯定不是看一遍书就能掌握的,在今后的学习和实践中如果有 ...

  9. 「ExLucas」学习笔记

    「ExLucas」学习笔记 前置芝士 中国剩余定理 \(CRT\) \(Lucas\) 定理 \(ExGCD\) 亿点点数学知识 给龙蝶打波广告 Lucas 定理 \(C^m_n = C^{m\% m ...

随机推荐

  1. Echart可视化学习(九)

    文档的源代码地址,需要的下载就可以了(访问密码:7567) https://url56.ctfile.com/f/34653256-527823386-04154f 官网找到类似实例, 适当分析,并且 ...

  2. mysql 连接表 内连接 inner

    字段去重  关键字distinct 去除重复记录 可配合分组函数使用 select distinct job,deptno from emp; 未使用 distinct之前 使用后: 笛卡尔积现象:当 ...

  3. rocketmq实现延迟队列精确到秒级实现方案2-时间轮和delay-file实现

    上图是通过RocketMQ源码分析一个实现原理方案示意图. 分为两个部分: 消息的写入消息的Schedule 在写入CommitLog之前,如果是延迟消息,按照每10分钟写入delayfile文件,对 ...

  4. manjaro20安装teamviewer出现sudo teamviewer –daemon start无响应

    问题 https://www.randomhacks.co.uk/the-teamviewer-daemon-is-not-running-please-start-the-daemon-ubuntu ...

  5. ThinkPad S5立体声混响以及语音识别

    smartaudio里面改成语音识别就可以是立体声混响了.但是微软语音识别在国内依然不好用,微软服务在国内太卡了. (联想总是多此一举,各种乱起八糟的软件,给用户造成困难,以前老机子驱动无线网卡锁在L ...

  6. Cesium入门7 - Adding Terrain - 添加地形

    Cesium入门7 - Adding Terrain - 添加地形 Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com ...

  7. VS code远程连接Linux 开发C++ 配置详细介绍

    VS code 远程连接服务器,编译C++ 一.前期准备 1.VS code安装 Remote-SSH插件 2.Windows安装SSH. 3.Linux服务器连接测试. a.接通测试使用ping命令 ...

  8. for each ……in

    使用一个变量迭代一个对象的所有属性值,对于每一个属性值,有一个指定的语句块被执行. 作为ECMA-357(E4X)标准的一部分,for each...in语句已被废弃,E4X中的大部分特性已被删除,但 ...

  9. golang中的排序算法实现

    1. 冒泡排序算法实现 package main import "fmt" func main() { values := []int{3, 98, 55, 46, 22, 3, ...

  10. HTML 页面的动态线条背景-三岁

    保存一个自己正在用的背景 会跟随鼠标变换的动态线条 以免后面还得找 挺好看的 效果图: 代码如下: <script type="text/javascript" color= ...