给你一个
n
×
m
n×m
n×m 的矩阵,行列从 1 开始标号,第 i 行 j 列的值是
(
i
−
1
)
∗
m
+
j
(i−1)∗m+j
(i−1)∗m+j
现在要求你实现以下操作。
- 交换两行。
- 交换两列。
- 求一个子矩阵的做 k 次前缀和之后的和,对 1 e 9 + 7 1e9+7 1e9+7取模
k ≤ 10 , n , m , q ≤ 1 e 5 k\le 10,n,m,q\le 1e5 k≤10,n,m,q≤1e5
- 题解
首先 i , j i,j i,j 表示的数为 ( i − 1 ) ∗ m + j (i-1)*m+j (i−1)∗m+j,那么可以看成是 a i + b j a_i+b_j ai+bj ,这样交换两行或是两列就是 a , b a,b a,b 数组中简单的交换
考虑一个点对整个和的贡献,这个贡献就是从 ( i , j ) (i,j) (i,j) 每次朝 ≥ i , j \ge i,j ≥i,j 可以是本身的方向走 k + 1 k+1 k+1 步到 ( x 2 , y 2 ) (x_2,y_2) (x2,y2) 的概率,发现 x , y x,y x,y 依然是独立的,那么方案数可以看成 c i d j c_id_j cidj
A n s = ∑ i = x 1 x 2 ∑ j = y 1 y 2 ( a i + b j ) c i d j Ans=\sum_{i=x_1}^{x_2}\sum_{j=y_1}^{y_2}(a_i+b_j)c_id_j Ans=i=x1∑x2j=y1∑y2(ai+bj)cidj
A n s = ( ∑ i = x 1 x 2 a i c i ) ( ∑ j = y 1 y 2 d j ) + ( ∑ j = y 1 y 2 b j d j ) ( ∑ i = x 1 x 2 c i ) Ans=(\sum_{i=x_1}^{x_2}a_ic_i)(\sum_{j=y_1}^{y_2}d_j)+(\sum_{j=y_1}^{y_2}b_jd_j)(\sum_{i=x_1}^{x_2}c_i) Ans=(i=x1∑x2aici)(j=y1∑y2dj)+(j=y1∑y2bjdj)(i=x1∑x2ci)
其中 c i = ( x 2 − i + k k ) c_i=\binom{x_2-i+k}{k} ci=(kx2−i+k),可以列求和,那么现在的问题就是快速求这么一个东西:
∑ i = x 1 y 1 a i ( x 2 − i + k k ) \sum_{i=x_1}^{y_1}a_i\binom{x_2-i+k}{k} i=x1∑y1ai(kx2−i+k)
一个套路是把组合数转成下降幂再转自然幂
( x 2 − i + k k ) = 1 k ! ( x 2 − i + k ) k ‾ \binom{x_2-i+k}{k}=\frac{1}{k!}(x_2-i+k)^{\underline k} (kx2−i+k)=k!1(x2−i+k)k
然后用 n m ‾ = ∑ i = 1 m s m , i ( − 1 ) m − i n i n^{\underline m}=\sum_{i=1}^m s_{m,i} (-1)^{m-i} n^i nm=∑i=1msm,i(−1)m−ini 化简
1 k ! ( x 2 − i + k ) k ‾ = \frac{1}{k!}(x_2-i+k)^{\underline k}= k!1(x2−i+k)k=
1 k ! ( x 2 − I + k ) k ‾ = 1 k ! ∑ i = 0 k ( − 1 ) k − i s k , i ( x 2 − I + k ) i \frac{1}{k!}(x_2-I+k)^{\underline k}=\frac{1}{k!}\sum_{i=0}^k(-1)^{k-i}s_{k,i}(x_2-I+k)^i k!1(x2−I+k)k=k!1i=0∑k(−1)k−isk,i(x2−I+k)i
1 k ! ∑ i = 0 k ( − 1 ) k − i s k , i ∑ j = 0 i ( i j ) ( x 2 + k ) j ( − I ) j − i \frac{1}{k!}\sum_{i=0}^k(-1)^{k-i}s_{k,i}\sum_{j=0}^i\binom{i}{j}(x_2+k)^j(-I)^{j-i} k!1i=0∑k(−1)k−isk,ij=0∑i(ji)(x2+k)j(−I)j−i
发现我们可以 O ( k 2 ) O(k^2) O(k2) 处理出 ( − I ) j (-I)^j (−I)j 的系数,那么我们用数据结构维护 a i ∗ ( − i ) k a_i*(-i)^k ai∗(−i)k 就可以了
单点修改区间查询,练了一波 z k w zkw zkw 线段树
询问复杂度 O ( k 2 + k l o g ( n ) ) O(k^2+klog(n)) O(k2+klog(n))
比较巧妙的地方是对 x , y x,y x,y 分别考虑,不好直接维护的东西转成多项式维护每一项的系数
#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
cs int N = 1e5 + 50, K = 15;
cs int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans = mul(ans,a); return ans; }
void Add(int &a, int b){ a = add(a, b); }
void Dec(int &a, int b){ a = dec(a, b); }
void Mul(int &a, int b){ a = mul(a, b); }
int sgn(int a){ return a & 1 ? Mod - 1 : 1; }
int n, m, q, A[N], B[N];
int C[K][K], S[K][K], fac[N], ifac[N];
int Binom(int n, int m){ if(n<0||m<0||n<m) return 0; return mul(fac[n],mul(ifac[n-m],ifac[m])); }
void prework(int n, int m){
C[0][0] = S[0][0] = fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
for(int i = 2; i <= m; i++) fac[i] = mul(fac[i-1], i);
ifac[m] = ksm(fac[m], Mod-2);
for(int i = m-1; i >= 2; i--) ifac[i] = mul(ifac[i+1], i+1);
for(int i = 1; i <= n; i++){
C[i][0] = 1;
for(int j = 1; j <= i; j++) C[i][j] = add(C[i-1][j], C[i-1][j-1]);
for(int j = 1; j <= i; j++) S[i][j] = add(S[i-1][j-1], mul(i-1, S[i-1][j]));
}
}
struct ZKW{
int vl[N<<2], M;
void build(int *a, int deg){
for(M=1;M<=deg;M<<=1);
for(int i=M+1; i<=M+deg; i++) vl[i]=a[i-M];
for(int i=M-1; i; i--) vl[i] = add(vl[i<<1], vl[i<<1|1]);
}
void modify(int x, int v){
vl[x+=M] = v;
for(x>>=1; x; x>>=1) vl[x] = add(vl[x<<1], vl[x<<1|1]);
}
int query(int l, int r){
int ans = 0;
for(l+=M-1,r+=M+1; l^r^1; l>>=1, r>>=1){
if(l&1^1) Add(ans, vl[l^1]);
if(r&1) Add(ans, vl[r^1]);
} return ans;
}
}Tx[11], Ty[11];
void build(){
static int a[N], b[N];
for(int i = 1; i <= n; i++) a[i] = A[i] = mul(i-1, m);
for(int i = 1; i <= m; i++) b[i] = B[i] = i;
for(int x = 0; x <= 10; x++){
Tx[x].build(a, n);
Ty[x].build(b, m);
for(int i = 1; i <= n; i++) Mul(a[i],dec(0,i));
for(int i = 1; i <= m; i++) Mul(b[i],dec(0,i));
}
}
int query(){
static int coe_x[K], coe_y[K];
memset(coe_x, 0, sizeof(coe_x));
memset(coe_y, 0, sizeof(coe_y));
int x1 = read(), y1 = read(), x2 = read(), y2 = read(), k = read();
for(int i = 0; i <= k; i++){
int coe = mul(ifac[k], mul(sgn(k-i), S[k][i]));
for(int j = 0, px = 1, py = 1; j <= i; j++, Mul(px, x2+k), Mul(py, y2+k)){
Add(coe_x[i-j], mul(coe, mul(C[i][j],px)));
Add(coe_y[i-j], mul(coe, mul(C[i][j],py)));
}
}
int Sx0 = 0, Sx1 = 0, Sy0 = 0, Sy1 = 0;
for(int i = 0; i <= k; i++){
Add(Sx1, mul(Tx[i].query(x1,x2), coe_x[i]));
Add(Sy1, mul(Ty[i].query(y1,y2), coe_y[i]));
} Sx0 = Binom(x2-x1+k+1, k+1); Sy0 = Binom(y2-y1+k+1,k+1);
int ans = add(mul(Sx0,Sy1),mul(Sy0,Sx1));
cout << ans << '\n';
}
void Swap_row(){
int x = read(), y = read();
swap(A[x], A[y]);
for(int i = 0, coe_x = 1, coe_y = 1; i <= 10; i++){
Tx[i].modify(x, mul(A[x],coe_x));
Tx[i].modify(y, mul(A[y],coe_y));
Mul(coe_x, dec(0,x)); Mul(coe_y, dec(0,y));
}
}
void Swap_clm(){
int x = read(), y = read();
swap(B[x], B[y]);
for(int i = 0, coe_x = 1, coe_y = 1; i <= 10; i++){
Ty[i].modify(x, mul(B[x],coe_x));
Ty[i].modify(y, mul(B[y],coe_y));
Mul(coe_x, dec(0,x)); Mul(coe_y, dec(0,y));
}
}
int main(){
n = read(), m = read(), q = read();
prework(10, 1e5 + 10);
build(); char op[3];
while(q--){
scanf("%s", op);
if(op[0] == 'Q') query();
if(op[0] == 'R') Swap_row();
if(op[0] == 'C') Swap_clm();
} return 0;
}