问题描述
LZK发明一个矩阵游戏,大家一起来玩玩吧,有一个N行M列的矩阵。第一行的数字是1,2,…M,第二行的数字是M+1,M+2…2*M,以此类推,第N行的数字是(N-1)*M+1,(N-1)M+2…NM。
例如,N=3,M=4的矩阵是这样的:
1 2 3
4 5 6
7 8 9
10 11 12
对于身为智慧之神的LZK来说,这个矩阵过于无趣.于是他决定改造这个矩阵,改造会进行K次,每次改造会将矩阵的某一行或某一列乘上一个数字,你的任务是计算最终这个矩阵内所有数字的和,输出答案对109+7取模。
题目大意
给定一个顺序标号的矩阵,有若干次操作使得某一行或某一列乘上某一个数,求最后只一个矩阵的所有数之和。
题解
我们用数组 标记第 行所有乘的数的积,用数组 标记第 列所有乘的数的积。
通过观察,可以发现第 行第 列的数字编号为: 。
显然对于每一个数,乘法的顺序不会对结果产生任何影响,因此我们可以先处理横向的乘法。
在做完横向的乘法以后,我们需要得到第j列所有数的和
,则有:
通过乘法分配律展开,得到:
再将右边部分的
提出,得到:
这样,我们就将算式化简为了两个部分,用字母
和
来换元,则:
其中
和
都是和
有关而和
无关的常量,因此我们可以在枚举j的使用用
的时间求出结果。这样,我们就可以用带有
和
的代数式来表示
的具体数值:
显然,这个算是也可以使用
的时间复杂度求出。那么我们就可以答案的表示方法:
由于需要枚举
,因此时间复杂度是
。
注意:
- 所有涉及具体数值的变量都要开 。
- 在做乘法和取模时,不要全部乘完再取模,要变乘变取模。
代码如下:
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const LL N = 1000010;
const LL P = 1e9 + 7;
LL n,m,k;
LL s[N],r[N];
int main(void)
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%lld %lld %lld",&n,&m,&k);
for (LL i=1;i<=n;++i) r[i] = 1;
for (LL i=1;i<=m;++i) s[i] = 1;
for (LL i=1;i<=k;++i)
{
LL x;
LL y;
char c = getchar();
while (c!='R' && c!='S') c = getchar();
scanf("%lld %lld",&x,&y);
if (c == 'S') s[x] = (s[x]*y)%P;
if (c == 'R') r[x] = (r[x]*y)%P;
}
LL sum1 = 0, sum2 = 0,ans = 0;
for (LL i=1;i<=n;++i)
sum1 = (r[i]*m%P*(i-1)%P + sum1)%P;
// sum1 = sigma r[i]*m*(i-1)
for (LL i=1;i<=n;++i)
sum2 = (sum2+r[i])%P;
// sum2 = sigma r[i]
for (LL j=1;j<=m;++j)
ans = (ans + (sum1+sum2*j%P)%P*s[j]%P)%P;
// ans = sigma (sum1+sum2*j)*s[j]
printf("%lld\n",ans);
return 0;
}