【模拟赛】随机跳走(消元,高斯消元)

题面

在这里插入图片描述
样例输入

3 1
512962164 361666497
363881557 376525373
176039948 197582789

样例输出

510253268
277873211
210117875

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

题解

由于数据全随机,所以其实在哪里出发都一样,最终出发点的影响一定会消失。每个点停留的概率会趋于稳定,而最终的稳定值,满足转移一次后概率不变。

那么,我们通过状态转移 d p i ∗ l i , j , d p i ∗ r i , j    →   d p k dp_i*l_{i,j},dp_i*r_{i,j}~~\rightarrow~dp_k dpili,j,dpiri,j   dpk 的矩阵可以高斯消元求出原来的 d p i dp_i dpi 。如果仅通过这 n n n 个方程消元,必定会得到 n n n 个 0,我们需要再加一个方程 ∑ d p i = 1 \sum dp_i=1 dpi=1

于是我们可以得到一个 O ( n 3 ) O(n^3) O(n3) 的做法,可以通过 2 k ≥ n 2k\geq n 2kn 的数据。

接下来我们发现, k k k 非常小,因此可以带状高斯消元。但是带状高斯消元常数过大,且 n n n 过大时不方便存储。其实我们可以先手动消元。

我们假定前 2 k 2k 2k 个数是未知变元,可以通过这 2 k 2k 2k 个数的线性组合表示出第 2 k + 1 2k+1 2k+1 个数,进而表示所有后面的变元。然后我们再根据这些线性组合的信息写出前 2 k 2k 2k 个数的 2 k 2k 2k 个方程加上方程 ∑ d p i = 1 \sum dp_i=1 dpi=1 ,就可以进行高斯消元了。

需要注意的是,每一个 l i , j , r i . j l_{i,j},r_{i.j} li,j,ri.j 都必须被用上,且 n ≤ 2 k n\leq 2k n2k 要特判。时间复杂度 O ( n k 2 ) O(nk^2) O(nk2) ,瓶颈在于求出每个数的线性组合表示。

CODE

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 20005
#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
#define eps 1e-9
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;
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 l[MAXN][55],r[MAXN][55];
int b[105],kk[MAXN][105];
int a[305][305];
void Gauss(int n) {
	for(int i = 0;i < n;i ++) {
		for(int j = i+1;j <= n;j ++) {
			if(a[j][i]) {swap(a[i],a[j]);break;}
		}
		int iv = qkpow(a[i][i],MOD-2);
		for(int j = i;j <= n;j ++) a[i][j] = a[i][j] *1ll* iv % MOD;
		for(int j = 0;j <= n;j ++) {
			if(j == i || !a[j][i]) continue;
			for(int k = n;k >= i;k --) {
				(a[j][k] += MOD-a[i][k]*1ll*a[j][i]%MOD) %= MOD;
			}
		}
	}return ;
}
int main() {
	n = read(); m = read();
	for(int i = 0;i < n;i ++) {
		int su = 0;
		for(int j = m;j > 0;j --) {
			l[i][j] = read();
			(su += l[i][j]) %= MOD;
		}
		for(int j = 1;j <= m;j ++) {
			r[i][j] = read();
			(su += r[i][j]) %= MOD;
		}
		l[i][0] = MOD-1;
		su = qkpow(su,MOD-2);
		for(int j = 1;j <= m;j ++) {
			l[i][j] = l[i][j] *1ll* su % MOD;
			r[i][j] = r[i][j] *1ll* su % MOD;
		}
	}
	if(2*m >= n) {
		for(int i = 0;i < n;i ++) {
			(a[i][i] += MOD-1) %= MOD;
			for(int j = 1;j <= m;j ++) {
				(a[(i+n-j)%n][i] += l[i][j]) %= MOD;
				(a[(i+j)%n][i] += r[i][j]) %= MOD;
			}
		}
		for(int i = 0;i <= n;i ++) a[n][i] = 1;
		Gauss(n);
		for(int i = 0;i < n;i ++) AIput(a[i][n],'\n');
		return 0;
	}
	for(int i = 0;i < m;i ++) b[i] = i,kk[i][i] = 1;
	for(int i = n-m;i < n;i ++) b[m+n-i-1] = i,kk[i][m+n-i-1] = 1;
	for(int i = m;i < n-m;i ++) {
		int iv = qkpow(l[i][m],MOD-2),p = i-m;
		for(int j = 0;j < m;j ++) {
			int y = (p+j)%n;
			int xs = l[y][j]*1ll*iv%MOD;
			for(int k = 0;k < 2*m;k ++) {
				kk[i][k] = (kk[y][k]*1ll*xs + kk[i][k]) % MOD;
			}
		}
		for(int j = 1;j <= m;j ++) {
			int y = (p+n-j)%n;
			int xs = r[y][j]*1ll*iv%MOD;
			for(int k = 0;k < 2*m;k ++) {
				kk[i][k] = (kk[y][k]*1ll*xs + kk[i][k]) % MOD;
			}
		}
		for(int j = 0;j < 2*m;j ++) kk[i][j] = (MOD-kk[i][j]) % MOD;
	}
	for(int i = 0;i < 2*m;i ++) {
		int x = b[i];
		int iv = qkpow(l[x][m],MOD-2),p = (x+n-m)%n;
		a[i][i] = 1;
		for(int j = 0;j < m;j ++) {
			int y = (p+j)%n;
			int xs = l[y][j]*1ll*iv%MOD;
			for(int k = 0;k < 2*m;k ++) {
				a[i][k] = (kk[y][k]*1ll*xs + a[i][k]) % MOD;
			}
		}
		for(int j = 1;j <= m;j ++) {
			int y = (p+n-j)%n;
			int xs = r[y][j]*1ll*iv%MOD;
			for(int k = 0;k < 2*m;k ++) {
				a[i][k] = (kk[y][k]*1ll*xs + a[i][k]) % MOD;
			}
		}
	}
	for(int i = 0;i < n;i ++) {
		for(int j = 0;j < 2*m;j ++) {
			a[m<<1][j] = (kk[i][j] + a[m<<1][j]) % MOD;
		}
	}
	a[m<<1][m<<1] = 1;
	Gauss(m<<1);
	for(int i = 0;i < n;i ++) {
		int as = 0;
		for(int j = 0;j < 2*m;j ++) {
			(as += a[j][m<<1]*1ll*kk[i][j]%MOD) %= MOD;
		}
		AIput(as,'\n');
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值