数字游戏 (number)
一道十分毒瘤的模拟题。代码很短,推我花了二三十分钟。
数字游戏
Description
不高兴找到了一个 N N N 行 M M M 列的矩阵。
矩阵的第一行数字为 1 1 1 , 2 2 2 , 3 3 3 ,…, M M M ;
第二行数字为 M + 1 M+1 M+1 , M + 2 M+2 M+2 ,…, 2 ∗ M 2*M 2∗M ;
…
第 N N N 行数字为 ( N − 1 ) ∗ M + 1 (N-1)*M+1 (N−1)∗M+1 , ( N − 1 ) ∗ M + 2 (N-1)*M+2 (N−1)∗M+2 ,…, N ∗ M N*M N∗M 。
例如,对于 N = 3 N = 3 N=3 , M = 4 M = 4 M=4:
1 2 3 4
5 6 7 8
9 10 11 12
当然这样的矩阵是无趣的,所以他选择了其中一行或一列,将其乘以非负数,会进行 K K K 次。最后他想知道矩阵中所有值的总和。由于总和可能会很大,需要总和模 1 0 9 + 7 10^9+7 109+7 。
Input
第一行三个正整数 N N N , M M M , K K K 。
以下 K K K 行中的每一行有两种描述:
-
" R R R X X X Y Y Y ", R R R 表示行,即将第 X X X ( 1 <= X <= N )行的所有数乘以 Y Y Y ;
-
" S S S X X X Y Y Y ", S S S表示列,即将第 X X X (1 <= X X X <= M M M )列的所有数乘以 Y Y Y 。
Sample Input
3 4 4
R 2 4
S 4 1
R 3 2
R 2 0
Output
输出矩阵所有数的和,并模 1 0 9 + 7 10^9+7 109+7 。
Sample Output
94
思路
一 概述
首先,对于每一行的整行和小列,我们分开处理,先列后行,且行和列的处理先后顺序对答案不产生影响。
然后,我们就可以用第一行来推出下面的所有行,所有数的和。
注意,以上两种思路是并行的,这也是为什么这道题如此恶心。
二 求第一行
对于第一行,就要处理我们刚刚分析到的,小列和小行。
首先,我们建立 2 个变量—— pre 和 sum 。
作用待会再说,先说说原因。
我们的目的,是用一行推出它的下一行。又因为它们是连续的,所以,我们可以得出 这一行的和 + 两行间的差 = 下一行的和。
而,所有行的和就是我们要求的。
则该式成立,我们接下来要求的,就是“这一行的和”以及“两行间的差”。回归我们刚刚建的变量, pre 代表的就是两行间的差 (请忽略这奇奇怪怪的变量名), sum 代表的就是这一行的和。
- 求 pre :
用样例给的数:( n = 3 , m = 4 )
1 2 3 4
5 6 7 8
在没有进行操作的时候,第 2 行的第 i 个数 = 第 1 行的第 i 个数 + m 。这个很好理解。
那当我给第 1 列乘上 2 呢?该怎么求?
第 1 列就会变成:
2 ——> 10 。
很明显,两者的差就变成了 m × 2 m \times 2 m×2 。那么当我们把每一列(也就是一整行)的差都加起来就会变成 ( l z m [ 1 ] + l z m [ 2 ] + . . . + l z m [ m ] ) ∗ m (lzm[1]+lzm[2]+...+lzm[m])*m (lzm[1]+lzm[2]+...+lzm[m])∗m ,用了提取公因数。
我们不急于乘以 m ,那么 p r e = l z m [ 1 ] + l z m [ 2 ] + . . . + l z m [ m ] pre=lzm[1]+lzm[2]+...+lzm[m] pre=lzm[1]+lzm[2]+...+lzm[m] 。
( lzm 是将每一列要乘的数都统计起来了,具体操作看代码。)
- 求 sum
sum 相对来说就好求多了(我们只需要求第一行的和且不考虑行要乘的数)。
第一行每一小列 i 的数为 i × l z m i i \times lzm_i i×lzmi ,举例理解:
第一行第一列的数为: 1 ;
我们给第一列乘 2 ,则第一行第一列的数就会变为 1 × 2 1 \times 2 1×2 。
综上,把一整行的求 sum 操作综合起来就是:
s u m = 1 × l z m 1 + 2 × l z m 2 + ⋯ + m × l z m m sum=1 \times lzm_1+2 \times lzm_2+ \cdots +m \times lzm_m sum=1×lzm1+2×lzm2+⋯+m×lzmm 。
回看这两个变量,我们都从 1 统计到了 m ,所以可以用 for 循环统计他们,就变成了:
int sum, pre;
sum = pre = 0;
for (int i = 1; i <= m; i++)
{
pre = (pre + lzm[i]) % mod;
sum = (sum + (i * lzm[i]) % mod) % mod;
}
lzm 是统计每列要乘的数, lzn 是统计每行要乘的数。
同时,你会发现,我们“先列后行”的思想就体现出来了。
三 推出后面 n − 1 n-1 n−1 行
我们用 ans 统计总和(也就是最终的答案)。
我们现在已经:
-
统计了每两行之间的差(差值是固定的);
-
算出了第一行乘完列之后的和(没有成行的数)。
首先对于第一行的 ans 值,易得它为 s u m × l z n 1 sum \times lzn_1 sum×lzn1 。
而紧接着对于接下来的 2 到 n 行,对于每一行我们都会类比刚才的操作:
-
求出 sum : s u m ← s u m + p r e × m sum \gets sum+pre \times m sum←sum+pre×m ( p r e × m pre \times m pre×m 这一步操作我们之前已经说过了);
-
求出该行的和并加到 ans 去:对于第 i 行会有 a n s ← a n s + s u m × l z n i ans \gets ans+sum \times lzn_i ans←ans+sum×lzni (类比第一行的 ans 值)。
就是这两步操作,是从 2 循环到 n ,具体实现就是:
ans = (ans + (lzn[1] * sum) % mod) % mod;
for (int i = 2; i <= n; i++)
{
sum = (sum + (pre * m) % mod) % mod;
ans = (ans + (sum * lzn[i]) % mod) % mod;
}
这样我们的答案 ans 也就统计出来了。
四 取模
一个小点被我拎出来讲了…
比赛我就是因为取模不正确而 WA 的啊!!!我的 50 pts …
不正确的取模:
int sum, pre;
sum = pre = 0;
for (int i = 1; i <= m; i++)
{
pre = (pre + lzm[i]) % mod;
sum = (sum + i * lzm[i]) % mod;
}
ans = (ans + lzn[1] * sum) % mod;
for (int i = 2; i <= n; i++)
{
sum = (sum + pre * m) % mod;
ans = (ans + sum * lzn[i]) % mod;
}
(就拿这两步作实例,前面的不放了。)
每一步都要取模!!只要乘了就要取模!!
所以,正确的取模是:
int sum, pre;
sum = pre = 0;
for (int i = 1; i <= m; i++)
{
pre = (pre + lzm[i]) % mod;
sum = (sum + (i * lzm[i]) % mod) % mod;
}
ans = (ans + (lzn[1] * sum) % mod) % mod;
for (int i = 2; i <= n; i++)
{
sum = (sum + (pre * m) % mod) % mod;
ans = (ans + (sum * lzn[i]) % mod) % mod;
}
最后只有 50 行的代码奉上(不用快读再压压行更少):
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
int n, m, k;
int ans = 0;
const int maxn = 1000005;
int lzn[maxn], lzm[maxn];
inline int read ()
{
int x = 1, s = 0;
char ch = getchar ();
while (ch < '0' or ch > '9'){if (ch == '-') x = -1; ch = getchar ();}
while (ch >= '0' and ch <= '9'){s = s * 10 + ch - '0'; ch = getchar ();}
return x * s;
}
signed main ()
{
n = read (), m = read (), k = read ();
for (int i = 0; i <= max (n, m); i++) lzn[i] = lzm[i] = 1;
while (k--)
{
char c[5];
int x, y;
scanf ("%s", c);
x = read (), y = read ();
if (c[0] == 'R') lzn[x] *= y;
else if (c[0] == 'S') lzm[x] *= y;
lzn[x] %= mod;
lzm[x] %= mod;
}
int sum, pre;
sum = pre = 0;
for (int i = 1; i <= m; i++)
{
pre = (pre + lzm[i]) % mod;
sum = (sum + (i * lzm[i]) % mod) % mod;
}
ans = (ans + (lzn[1] * sum) % mod) % mod;
for (int i = 2; i <= n; i++)
{
sum = (sum + (pre * m) % mod) % mod;
ans = (ans + (sum * lzn[i]) % mod) % mod;
}
printf ("%lld\n", ans % mod);
return 0;
}
—— E n d End End——
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】