1. 程式人生 > 實用技巧 >矩陣遊戲

矩陣遊戲

矩陣遊戲(數學規律 \(\star\star \))

  • 時限:\(1s\) 記憶體:\(256M\)

Descrption

  • \(LZK\)發明一個矩陣遊戲,大家一起來玩玩吧,有一個\(N\)行M列的矩陣。第一行的數字是\(1,2,…M\),第二行的數字是\(M+1,M+2…2*M\),以此類推,第 \(N\) 行的數字是\((N-1)*M+1,(N-1)*M+2…N*M\)

  • 例如,\(N=3,M=4\)的矩陣是這樣的:

    1	2	3	4
    5	6	7	8
    9	10	11	12
    
  • 對於身為智慧之神的 \(LZK\) 來說,這個矩陣過於無趣.於是他決定改造這個矩陣,改造會進行 \(K\)

    次,每次改造會將矩陣的某一行或某一列乘上一個數字,你的任務是計算最終這個矩陣內所有數字的和,輸出答案對\(10^9+7\)取模。

Input

  • 第一行包含三個正整數\(N、M、K\),表示矩陣的大小與改造次數。接下來的行,每行會是如下兩種形式之一:
    • \(R\ X\ Y\),表示將矩陣的第\(X(1 ≤ X ≤ N)\)行變為原來的\(Y(0 ≤ Y ≤10^9)\)倍.
    • \(S\ X\ Y\),表示將矩陣的第\(X(1 ≤ X ≤ M)\)列變為原來的\(Y(0 ≤ Y ≤10^9)\)倍.

Output

  • 輸出一行一個整數,表示最終矩陣內所有元素的和對\(10^9+7\)取模的結果。

Sample Input

game.in
3 4 4 
R 2 4 
S 4 1 
R 3 2 
R 2 0
game.out
94
​```

Sample Output

game.in
2 4 4 
S 2 0 
S 2 3 
R 1 5 
S 1 3
game.out
80

Hint

  • 來源:

分析

方法一:

  • 顯然對於每一個數,乘法的順序不會對結果產生任何影響,我們用陣列 \(r_i\) 標記第 \(i\) 行所有乘的數的積,用陣列 \(s_i\) 標記第 \(j\) 列所有乘的數的積。

  • 通過觀察,可以發現第 \(i\) 行第 \(j\) 列的數字為:\((i-1)*m+j\)

  • 因為先做行的乘法或先做列的乘法,結果都一樣,所以我們可以先做行的乘法。然後我們可以把同一列的所有數都加起來,令 \(sum_j\)

    :表示做了行乘法後,第 \(j\) 列的和。

    \[sum_j=\sum_{i=1}^{n}r_i\times((i-1)\times m+j) \\ \Rightarrow sum_j=\sum_{i=1}^{n}r_i\times (i-1)*m+\sum_{i=1}^{n}r_i \times j \\ \Rightarrow sum_j=\sum_{i=1}^n r_i\times(i-1)*m+j\times \sum_{i=1}^{n} r_i \]

  • \(k_1=\sum_{i=1}^n r_i\times (i-1)*m,k_2=\sum_{i=1}^{n} r_i\) ,顯然 \(k_1,k_2\) 是跟 \(i\) 相關數。 \(sum_j=k_1+j\times k_2\)

Code

#include <bits/stdc++.h>
typedef long long LL;
const int maxn=1e6+5;
const LL Mod=1e9+7;
LL s[maxn],r[maxn];
int n,m;
void Init(){
    int k;scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;++i)r[i]=1;
    for(int i=1;i<=m;++i)s[i]=1;
    while(k--){
        LL x,y;char ch;
        scanf(" %c%lld%lld",&ch,&x,&y);
        if(ch=='S')s[x]=(s[x]*y)%Mod;
        if(ch=='R')r[x]=(r[x]*y)%Mod;
    }
}
void Solve(){
    LL sum1=0,sum2=0,ans=0;
    for(int i=1;i<=n;++i)//第一部分:sigma r[i]*(i-1)*m
        sum1=(r[i]*m%Mod*(i-1)%Mod+sum1)%Mod;
    for(int i=1;i<=n;++i)//第二部分:sigma r[i]
        sum2=(sum2+r[i])%Mod;
    for(int i=1;i<=m;++i)//ans=sigma sumi*s[j] 
        ans=(ans+(sum1+sum2*i%Mod)%Mod*s[i]%Mod)%Mod;
    printf("%lld\n",ans);
}
int main(){
    Init();
    Solve();
    return 0;
}

方法二:

  • 經過分析可知:
    1. 操作可以任意順序進行,不會影響最終結果。
    2. 每一行都是一個等差數列,且一開始公差都為\(1\)
    3. 每一列相加得到的數列也是等差數列,公差為所有等差數列的公差之和。
  • 我們就可以考慮先進行的操作,再進列的操作。每一行的數乘上一個數後仍然是等差數列,公差增加相應倍數。這樣,對於每一行,我們只用維護第一項和它的公差就好了。
  • 當行的操作弄完後,將所有等差數列的第一項相加,得到第一列的和,再將公差相加,得到每一列和的公差,就可以求出每一列的和,剩下的就是直接對列的和進行乘法就好了。

Code

#include <bits/stdc++.h>
typedef long long LL;
const int maxn=1e6+5;
const LL Mod=1e9+7;
LL col[maxn];//col[i]記錄第i列的乘數
LL a[maxn],b[maxn];//a[i],b[i]分別為第i行首項和公差
LL sum[maxn];//sum[i]表示第i列之和
int n,m;
void Init(){
    int k;scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;++i){
        a[i]=(1LL*(i-1)*m+1)%Mod;//第i行的首項
        b[i]=1;//第i行的公差    
    }
    for(int i=1;i<=m;++i)col[i]=1;
    while(k--){
        LL x,y;char ch;
        scanf(" %c%lld%lld",&ch,&x,&y);
        if(ch=='S')
            col[x]=(col[x]*y)%Mod;
        if(ch=='R'){
            a[x]=(a[x]*y)%Mod;
            b[x]=(b[x]*y)%Mod;
        }
    }
}
void Solve(){
    LL d=0,ans=0;
    for(int i=1;i<=n;++i){//求出第一列之和
        sum[1]=(sum[1]+a[i])%Mod;//首項
        d=(d+b[i])%Mod;//公差
    }
    for(int i=2;i<=m;++i)//利用等差數列公式求出其他列之和
        sum[i]=(sum[i-1]+d)%Mod;
    for(int i=1;i<=m;++i)
        ans=(ans+sum[i]*col[i]%Mod)%Mod;
    printf("%lld\n",ans);
}
int main(){
    Init();
    Solve();
    return 0;
}