【省选模拟】矩阵求和(斯特林数)(组合数转下降幂)(ZKW线段树)

H e r e Here Here

给你一个 n × m n×m n×m 的矩阵,行列从 1 开始标号,第 i 行 j 列的值是 ( i − 1 ) ∗ m + j (i−1)∗m+j (i1)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 k10,n,m,q1e5


  • 题解
    首先 i , j i,j i,j 表示的数为 ( i − 1 ) ∗ m + j (i-1)*m+j (i1)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=x1x2j=y1y2(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=x1x2aici)(j=y1y2dj)+(j=y1y2bjdj)(i=x1x2ci)
    其中 c i = ( x 2 − i + k k ) c_i=\binom{x_2-i+k}{k} ci=(kx2i+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=x1y1ai(kx2i+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} (kx2i+k)=k!1(x2i+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)mini 化简
    1 k ! ( x 2 − i + k ) k ‾ = \frac{1}{k!}(x_2-i+k)^{\underline k}= k!1(x2i+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(x2I+k)k=k!1i=0k(1)kisk,i(x2I+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=0k(1)kisk,ij=0i(ji)(x2+k)j(I)ji
    发现我们可以 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值