题解:P17730 [SCP-J 2026] 数组 / array

· · 题解

出题人题解。

12pts 1~3

暴力模拟每个 a 数组即可。

40pts 1~7 + 19~21

### 12pts 8\~10 暴力修改暴力查,或者写个线段树,$O(n(p+q))$ 能过。 ### 8pts 11\~12 注意到每个询问可以被拆成两个 $a_{i,j}$ 的查询。 $O(n^2)$ 预处理后 $O(1)$ 查询即可。 ## 8pts 17\~18 考虑 $a_{i,l}$ 对 $a_{i+j,l+k}$ 的贡献,设这个值是 $s_{j,k}$,易得 $s_{0,0}=1,s_{j,k}=s_{j-1,k}+s_{j,k-1}$ 原因是 $a_{i,j}=a_{i-1,j}+a_{i,j-1}$。到这里整个题就做出来一半了。 修改时暴力修改,查询时暴力查询即可。 ## 8pts 15\~16 - 考虑每次修改暴力维护 $a_0$ 数组。设修改 $a_{i+j,l+k}$ 会对 $a_{i,l}$ 造成 $s_{j,k}$ 的贡献,~~这不是二项式反演吗,原谅我出这个题的时候没学到这。~~我们进行简单思考,发现此时有 $s_{0,0}=0$,$s_{j,k}=s_{j+1,k}-s_{j+1,k-1}$。这不就是杨辉三角左对齐之后偶数列变成负数吗?或者预处理出来 $s$,对于每个修改,暴力维护 $a_0$ 即可。 把以上两种做法拼起来,就可以进行修改的时候暴力改 $a_0$,然后查询暴力查即可获得 $100pts$。 ## 8pts 13\~14 对第二种做法的启发性部分分。 - 我们考虑把在这个询问前的修改都记下来,然后考虑每个修改对查询造成什么贡献。每个查询是两个单点修改。然后我们可以在 $O(p)$ 时间内回答这一个询问。 - 操作分块一下,块结束之后暴力还原 $a_0$,直接做到 $O(\frac{(p+q)}{B}n^2+B(q+p))$,由基本不等式发现 $B=n$ 时取得最优,为 $O((p+q)n)$。 顺带一提,据说有验题人用这个方法解的。 #### tips 1.注意开 `long long`。 2.各种下标减来减去别减不明白。 给这么多具有启发性和暴力部分分是不是很好(bushi)。 写在后面。当时我以为自己发明了一种新算法,就是把一定的修改分组,等到一定时候重构,结果一看好多人已经发明过了。叫什么操作分块 / 根号重构。[空悲切](https://www.luogu.com.cn/record/282236871)。 附一份正常做法的代码吧。 :::info[Code] ```c++ #define _CRT_SECURE_NO_WARNINGS #include<bitset> #include<vector> #include<string> #include<iostream> #include<queue> #include<stack> #include <cassert> #include <cstring> #include <string> using namespace std; /**/ #define int long long /**/ using ll = long long; const int MXN = 350; const int P = 1e9 + 7; int C[MXN][MXN],q[MXN][MXN]; int n, q1, q2; int A0[MXN]; void init() { for (int i = 0; i <= n; i++) { q[0][i] =q[i][0]= 1; } for(int i=1;i<=n;i++) for (int j = 1; j <= n; j++) { q[i][j] = q[i][j - 1] + q[i - 1][j]; q[i][j] %= P; } C[0][1] = 1; for(int i=1;i<=n;i++) for (int j = 1; j <= n; j++) { C[i][j] = C[i - 1][j] + C[i - 1][j - 1]; C[i][j] %= P; } } int o, x, l, r, k; void mer(int _x, int v, int p) { for (int i = 0; i + p <= n; i++) { A0[i + p] += 1LL * v * C[_x][i+1] * ((i&1) ? -1 : 1); A0[i + p] %= P; A0[i + p] += P; A0[i + p] %= P; } } int que(int _x, int p) { int c = 0, ans = 0; for (int i = p; i >= 1; i--) { ans += 1LL * A0[i] * q[_x - 1][c++]; ans %= P; } return ans; } void solve() { cin >> n >> q1 >> q2; init(); for (int i = 1; i <= n; i++)cin >> A0[i]; int qs = q1 + q2; while (qs--) { cin >> o >> x >> l >> r; if (o == 1) { cin >> k; if (x) { mer(x - 1, k, l); mer(x - 1, -k, r + 1); } else for (int i = l; i <= r; i++) A0[i] += k, A0[i] %= P, A0[i] += P, A0[i] %= P; } else { cout << ((que(x+1,r)-que(x+1,l-1))%P+P)%P<<'\n'; } } } signed main() { string i_ = ".in"; int T; /*/ cin>>T; /*/ T = 1; /**/ while (T--) { solve(); } return 0; } ``` :::