[LOJ#6617][THUPC2019]摆家具(矩阵乘法 + 子集和变换)

Address

Solution

Task 1

  • 先解决一个小问题
  • 如何求 T T T 次操作之后,对于所有的 0 ≤ i ≤ k 0\le i\le k 0ik ,求出最后恰好有 i i i 个家具不在原来的房间内的方案数
  • 容易设计一个 DP
  • f [ i ] [ j ] f[i][j] f[i][j] 表示 i i i 次操作之后,恰好有 j j j 个家具不在原来的房间内的方案数
  • 转移(自行理解)
  • f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1
  • f [ i ] [ j ] = f [ i − 1 ] [ j ] × j × ( n − 2 ) + f [ i − 1 ] [ j − 1 ] × ( k − j + 1 ) × ( n − 1 ) + f [ i − 1 ] [ j + 1 ] × ( j + 1 ) f[i][j]=f[i-1][j]\times j\times(n-2)+f[i-1][j-1]\times(k-j+1)\times(n-1)+f[i-1][j+1]\times(j+1) f[i][j]=f[i1][j]×j×(n2)+f[i1][j1]×(kj+1)×(n1)+f[i1][j+1]×(j+1)
  • 可以预处理转移矩阵 A A A 和初始向量 S t St St
  • 取参数 S S S 使得 S 2 + S − 1 ≤ max ⁡ T S^2+S-1\le\max T S2+S1maxT max ⁡ T = 998244352 \max T=998244352 maxT=998244352
  • 然后对于所有的 0 ≤ i ≤ S 0\le i\le S 0iS 预处理出矩阵 A i A^i Ai ( A S ) i (A^S)^i (AS)i
  • 那么
  • A n × S t = ( A S ) ⌊ n S ⌋ × ( A n   m o d   S × S t ) A^n\times St=(A^S)^{\lfloor\frac nS\rfloor}\times(A^{n\bmod S}\times St) An×St=(AS)Sn×(AnmodS×St)
  • 可以 O ( S k 3 ) O(Sk^3) O(Sk3) 预处理 O ( k 2 ) O(k^2) O(k2) 单次询问,其中 S = O ( P ) S=O(\sqrt P) S=O(P )

Task 2

  • 对于每个 S S S i i i ,预处理出与方案 S S S 恰好有 i i i 个家具的摆放位置不同的所有方案的得分之和
  • 考虑和子集和变换一样,设 F [ S ] [ i ] [ j ] F[S][i][j] F[S][i][j] 表示方案的前 i i i 个家具中恰好有 j j j 个家具摆放位置与 S S S 不同,后 k − i k-i ki 个家具的摆放位置与 S S S 都一样的方案数
  • F [ S ] [ 0 ] [ 0 ] = a S F[S][0][0]=a_S F[S][0][0]=aS
  • a S a_S aS 为方案 S S S 的得分)
  • F [ S ] [ i ] [ j ] = F [ S ] [ i − 1 ] [ j ] + ∑ r e p l a c e ( S , i , h ) ≠ S F [ r e p l a c e ( S , i , h ) ] [ i − 1 ] [ j − 1 ] F[S][i][j]=F[S][i-1][j]+\sum_{replace(S,i,h)\ne S}F[replace(S,i,h)][i-1][j-1] F[S][i][j]=F[S][i1][j]+replace(S,i,h)̸=SF[replace(S,i,h)][i1][j1]
  • r e p l a c e ( S , i , h ) replace(S,i,h) replace(S,i,h) 表示把方案 S S S 中第 i i i 件家具移到房间 h h h 得到的方案
  • ∑ \sum 内的东西可以利用前缀和等方法求
  • O ( k 2 n k ) O(k^2n^k) O(k2nk)

Solution

  • 回到问题
  • 根据常识, T T T 次操作之后,对于每个 i i i ,都满足:对于所有的方案 S S S 满足 a a a S S S 中恰好 i i i 个家具的所处房间不同,从初始状态 a a a 到达末状态 S S S 的方案数都一样
  • 于是对于一个特定的状态 S S S ,如果 a a a S S S 中恰好 i i i 个家具摆放位置不同
  • 那么从状态 a a a 到达状态 S S S 的方案数可以用 Task 1 中的矩阵乘法算出,为
  • f [ T ] [ i ] C k i ( n − 1 ) i \frac{f[T][i]}{C_k^i(n-1)^i} Cki(n1)if[T][i]
  • 其中 C C C 为组合数
  • 于是一次询问 ( a , T ) (a,T) (a,T) 答案为
  • ∑ i = 0 k f [ T ] [ i ] C k i ( n − 1 ) i × F [ S ] [ k ] [ i ] \sum_{i=0}^k\frac{f[T][i]}{C_k^i(n-1)^i}\times F[S][k][i] i=0kCki(n1)if[T][i]×F[S][k][i]
  • 总复杂度 O ( P k 3 + k 2 n k + q k 2 ) O(\sqrt Pk^3+k^2n^k+qk^2) O(P k3+k2nk+qk2)

Code

  • 常数巨大,见谅
#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

const int N = 1e6 + 5, L = 4e4 + 5, M = 22, ZZQ = 998244353;

int n, k, q, a[N], lstans = 1, f[N][M], pw1[M], pw[M], C[M][M], p1[M], p2[M],
dw1[M], sum[N][M];

inline void add(int &a, const int &b)
{
	a += b;
	if (a >= ZZQ) a -= ZZQ;
}

int qpow(int a, int b)
{
	int res = 1;
	while (b)
	{
		if (b & 1) res = 1ll * res * a % ZZQ;
		a = 1ll * a * a % ZZQ;
		b >>= 1;
	}
	return res;
}

struct matrix
{
	int n, m, a[M][M];
	
	matrix() {}
	matrix(int _n, int _m) :
		n(_n), m(_m) {memset(a, 0, sizeof(a));}
	
	friend inline matrix operator * (const matrix &a, const matrix &b)
	{
		matrix res = matrix(a.n, b.m);
		for (int i = 1; i <= res.n; i++)
			for (int j = 1; j <= res.m; j++)
				for (int k = 1; k <= a.m; k++)
					add(res.a[i][j], 1ll * a.a[i][k] * b.a[k][j] % ZZQ);
		return res;
	}
} s0[L], s1[L], St, Tr;

int main()
{
	int S, T;
	read(n); read(k); read(q);
	for (int i = 0; i <= k; i++) C[i][0] = 1;
	for (int i = 1; i <= k; i++)
		for (int j = 1; j <= i; j++)
			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % ZZQ;
	pw[0] = pw1[0] = dw1[0] = 1;
	for (int i = 1; i <= k; i++)
	{
		pw[i] = pw[i - 1] * n; pw1[i] = pw1[i - 1] * (n - 1);
		dw1[i] = qpow(pw1[i], ZZQ - 2);
		C[k][i] = qpow(C[k][i], ZZQ - 2);
	}
	for (int i = 0; i < pw[k]; i++) read(a[i]);
	for (int i = 0; i < pw[k]; i++) f[i][0] = a[i];
	for (int i = 1; i <= k; i++)
	{
		if (n == 2) for (int j = i; j >= 1; j--)
			for (int S = 0; S < pw[k]; S++)
				add(f[S][j], f[S ^ (1 << i - 1)][j - 1]);
		else for (int j = 0; j <= i; j++)
			for (int S = 0; S < pw[k]; S++)
			{
				int id = S / pw[i - 1] % n;
				sum[S][j] = f[S][j];
				if (id) add(sum[S][j], sum[S - pw[i - 1]][j]);
				add(f[S][j], sum[S + (n - 1 - id) * pw[i - 1]][j - 1]);
				add(f[S][j], ZZQ - sum[S][j - 1]);
				if (id) add(f[S][j], sum[S - pw[i - 1]][j - 1]);
			}
	}
	s0[0] = Tr = matrix(k + 1, k + 1);
	for (int i = 1; i <= k + 1; i++) s0[0].a[i][i] = 1;
	s1[0] = s0[0];
	for (int i = 0; i <= k; i++)
	{
		if (i) Tr.a[i + 1][i] = (k - i + 1) * (n - 1);
		Tr.a[i + 1][i + 1] = i * (n - 2);
		if (i < k) Tr.a[i + 1][i + 2] = i + 1;
	}
	St = matrix(k + 1, 1); St.a[1][1] = 1;
	for (int i = 1; i <= 33333; i++)
		s0[i] = s0[i - 1] * Tr;
	for (int i = 1; i <= 33333; i++)
		s1[i] = s1[i - 1] * s0[33333];
	while (q--)
	{
		read(S); read(T);
		T = 1ll * T * lstans % ZZQ;
		matrix A = s1[T / 33333] * (s0[T % 33333] * St);
		lstans = 0;
		for (int i = 0; i <= k; i++)
			add(lstans, 1ll * A.a[i + 1][1] * dw1[i] % ZZQ * C[k][i] % ZZQ
			* f[S][i] % ZZQ);
		printf("%d\n", lstans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值