【ÑÖÏ模拟赛】花萎(矩阵加速,循环卷积,高斯消元)

题面

4s ,512mb
在这里插入图片描述
在这里插入图片描述

题解

d p [ x ] [ y ] dp[x][y] dp[x][y] 表示到达 x , y x,y x,y 的期望时间, d p [ 0 ] [ 0 ] = 0 dp[0][0]=0 dp[0][0]=0,容易得到转移式
d p [ x ] [ y ] = p ∗ d p [ x − 1  ⁣ ⁣ ⁣ ⁣ m o d    n ] [ y ] + q ∗ d p [ x ] [ y − 1  ⁣ ⁣ ⁣ ⁣ m o d    m ] + 1 dp[x][y]=p*dp[x-1\!\!\!\!\mod n][y]+q*dp[x][y-1\!\!\!\!\mod m]+1 dp[x][y]=pdp[x1modn][y]+qdp[x][y1modm]+1

但是由于 x x x 过大,而 y < 400 y < 400 y<400 ,所以这不是我们想要的式子,我们需要主元少些方便高斯消元。我们得将 d p [ x ] [ y − 1 ] dp[x][y-1] dp[x][y1] 展开,一直展开到无穷,没有 x x x 项为止:
d p [ x ] [ y ] = 1 1 − q + ∑ i = 0 m − 1 d p [ x − 1 ] [ y − i  ⁣ ⁣ ⁣ ⁣ m o d    m ] ∗ p   q i 1 − q m dp[x][y]=\frac{1}{1-q}+\sum_{i=0}^{m-1}dp[x-1][y-i\!\!\!\!\mod m]*\frac{p\,q^i}{1-q^{m}} dp[x][y]=1q1+i=0m1dp[x1][yimodm]1qmpqi

这是个从 { { d p [ x − 1 ] [ y ] } , 1 } \{\{dp[x-1][y]\},1\} {{dp[x1][y]},1} { { d p [ x ] [ y ] } , 1 } \{\{dp[x][y]\},1\} {{dp[x][y]},1} 的线性变换,所以我们可以用矩阵乘法。算出转移矩阵的 n − 1 n-1 n1 次方后,定 d p [ 0 ] [ 0 ∼ m − 1 ] dp[0][0\sim m-1] dp[0][0m1] 为主元,我们就可以得到 m m m 个方程的系数,然后高斯消元求出主元。

过程中预处理出转移矩阵的 1 , 2 , 4 , . . . 2 log ⁡ n 1,2,4,...2^{\log n} 1,2,4,...2logn 次方,在询问的时候倍增求 d p [ X i ] [ Y i ] dp[X_i][Y_i] dp[Xi][Yi],矩阵乘法可以从 O ( m 3 ) O(m^3) O(m3) 变为 O ( m 2 ) O(m^2) O(m2) ,老套路了。

这样一来,时间复杂度是 O ( m 3 log ⁡ n + t m 2 log ⁡ n ) O(m^3\log n+tm^2\log n) O(m3logn+tm2logn) ,刚好过不了。

回过头来观察式子,我们发现 p   q i 1 − q m \frac{p\,q^i}{1-q^{m}} 1qmpqi y y y 没有关系,也就是说,这就是个循环卷积。

f k = ∑ i = 0 m − 1 d p [ k ] [ i ] ⋅ x i ,    g = ∑ i = 0 m − 1 p   q i 1 − q m ⋅ x i ,    h = ∑ i = 0 m − 1 1 1 − q ⋅ x i f_{k}=\sum_{i=0}^{m-1}dp[k][i]\cdot x^i,~~g=\sum_{i=0}^{m-1}\frac{p\,q^i}{1-q^{m}}\cdot x^i,~~h=\sum_{i=0}^{m-1}\frac{1}{1-q}\cdot x^i fk=i=0m1dp[k][i]xi,  g=i=0m11qmpqixi,  h=i=0m11q1xi ,那么
f k = f k − 1 ∗ g + h f_{k}=f_{k-1}*g+h fk=fk1g+h

这是关于多项式的线性变换,我们打包 2×2 的多项式矩阵进行矩阵乘法,用 NTT 卷积模拟循环卷积,一次变换的复杂度就从 O ( m 3 ) O(m^3) O(m3) 变成了 O ( m log ⁡ m ) O(m\log m) O(mlogm)

最终复杂度为 O ( m 3 + t m log ⁡ m log ⁡ n ) O(m^3+tm\log m\log n) O(m3+tmlogmlogn)

CODE

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
#define MAXN 405
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
// #define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

const int MOD = 998244353;
int n,m,s,o,k;
inline int qkpow(int a,int b) {
	int res = 1;
	while(b > 0) {
		if(b & 1) res = res*1ll*a % MOD;
		a = a *1ll* a % MOD; b >>= 1;
	}return res;
}
int xm[MAXN<<2],om,rev[MAXN<<2];
void NTT(int *s,int n,int op) {
	for(int i = 1;i < n;i ++) {
		rev[i] = (rev[i>>1]>>1) | ((i&1) ? (n>>1):0);
		if(rev[i] < i) swap(s[rev[i]],s[i]);
	} om = qkpow(3,(MOD-1)/n); xm[0] = 1;
	if(op < 0) om = qkpow(om,MOD-2);
	for(int i = 1;i <= n;i ++) xm[i] = xm[i-1]*1ll*om % MOD;
	for(int k = 2,t = n>>1;k <= n;k <<= 1,t >>= 1) {
		for(int j = 0;j < n;j += k) {
			for(int i = j,l = 0;i < j+(k>>1);i ++,l += t) {
				int A = s[i],B = s[i+(k>>1)];
				s[i] = (A + xm[l]*1ll*B) % MOD;
				s[i+(k>>1)] = (A +MOD- xm[l]*1ll*B % MOD) % MOD;
			}
		}
	}
	if(op < 0) {
		LL v = qkpow(n,MOD-2);
		for(int i = 0;i < n;i ++) s[i] = s[i] * v % MOD;
	} return ;
}
int ta[MAXN<<2],tb[MAXN<<2];
void addmult(int *C,int *A,int *B,int m) {
	int le = 1; while(le < m+m) le <<= 1;
	for(int i = 0;i < le;i ++) ta[i] = tb[i] = 0;
	for(int i = 0;i < m;i ++) ta[i] = A[i],tb[i] = B[i];
	NTT(ta,le,1); NTT(tb,le,1);
	for(int i = 0;i < le;i ++) ta[i] = ta[i] *1ll* tb[i] % MOD;
	NTT(ta,le,-1);
	for(int i = 0,j = 0;i < le;i ++,j = (j+1 == m ? 0:j+1)) {
		C[j] += ta[i]; if(C[j] >= MOD) C[j] -= MOD;
	} return ;
}
int p,q;
struct mat{
	int s[2][2][401],n,m;
	mat(){memset(s,0,sizeof(s));n=m=0;}
}A,B,b[35];
mat operator * (mat a,mat b) {
	mat c; c.n = a.n;c.m = b.m;
	for(int i = 0;i < a.n;i ++) {
		for(int k = 0;k < a.m;k ++) {
			if(a.s[i][k])
			for(int j = 0;j < b.m;j ++) {
				addmult(c.s[i][j],a.s[i][k],b.s[k][j],m);
			}
		}
	} return c;
}
int a[MAXN][MAXN];
void Gauss(int n) {
	for(int i = 0;i < n;i ++) {
		for(int j = i+1;j < n;j ++) {
			while(a[j][i]) {
				int nm = a[i][i] / a[j][i];
				for(int k = i;k <= n;k ++) {
					(a[i][k] += MOD - a[j][k]*1ll*nm % MOD);
					if(a[i][k] >= MOD) a[i][k] -= MOD;
					swap(a[i][k],a[j][k]);
				}
			}
		}
	}
	for(int i = n-1;i >= 0;i --) {
		for(int j = i+1;j < n;j ++) {
			(a[i][n] += MOD - a[i][j]*1ll*a[j][n]%MOD) %= MOD;
			a[i][j] = 0;
		}
		a[i][n] = a[i][n] *1ll* qkpow(a[i][i],MOD-2) % MOD;
		a[i][i] = 1;
	} return ;
}
int main() {
	freopen("huawei.in","r",stdin);
	freopen("huawei.out","w",stdout);
	n = read();m = read();
	int P = read(),Q = read(),pq = qkpow((P+Q)%MOD,MOD-2);
	p = P*1ll*pq % MOD; q = Q*1ll*pq % MOD;
	A.n = A.m = B.n = B.m = 2;
	A.s[0][0][0] = A.s[1][1][0] = 1;
	B.s[1][1][0] = 1;
	swap(p,q);
	int pi = qkpow((MOD+1-p)%MOD,MOD-2),pw = qkpow(p,m),pwi = qkpow((MOD+1-pw)%MOD,MOD-2);
	B.s[1][0][0] = 1;
	for(int j = 0,po = pwi*1ll*q%MOD;j < m;j ++,po = po*1ll*p % MOD) {
		B.s[0][0][j] = po;
	}
	int nn = n-1;
	for(int i = 0;i <= 30 && nn > 0;i ++) {
		b[i] = B;
		if(nn & 1) {
			A = A * B;
		}
		B = B * B; nn >>= 1;
	}
	mat tl; tl.n = 1; tl.m = 2;
	for(int i = 0;i < m;i ++) tl.s[0][1][i] = pi;
	for(int i = 0;i < m;i ++) {
		a[i][i] = 1;
		if(i == 0) continue;
		mat t2 = tl * A;
		a[i][m] = (t2.s[0][0][i]*1ll*q % MOD + 1) % MOD;
		a[i][i-1] = MOD - p;
		for(int j = 0;j < m;j ++) {
			(a[i][j] += MOD - A.s[0][0][(i+m-j)%m]*1ll*q % MOD) %= MOD;
		}
	}
	Gauss(m);
	A = tl;
	for(int i = 0;i < m;i ++) A.s[0][0][i] = a[i][m];
	int T = read();
	while(T --) {
		s = read();o = read();
		mat m0 = A;
		for(int i = 0;i <= 30;i ++) {
			if(s & 1) m0 = m0 * b[i];
			s >>= 1;
		}
		AIput(m0.s[0][0][o],'\n');
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值