首页 > 其他分享 >杂项——矩阵加速(进阶)

杂项——矩阵加速(进阶)

时间:2024-09-17 19:01:56浏览次数:1  
标签:tem 进阶 int rep 矩阵 st 杂项 s1 Mat

前言:

在之前已经学习过矩阵快速幂的用法,那些只是基础。

在ICPC中大多数难度较高,且并不是简单的只需要常数的矩阵或者简单的图上问题,而是结合dp方程去推导出来转移矩阵。

trick:

例题:

链接:https://ac.nowcoder.com/acm/contest/88880/E
来源:牛客网

给出两个整数 \(n,k\),有一个正整数集合 \(X=\{1,2,3,\cdots,n-1,n\}\)。你需要求出满足以下条件的映射 \(f: X \to X\) 的个数模 \(998244353\) 的值:

  • \(f \circ f=I_X\),即 \(f(f(x))=x\)

  • \(|f(x)-x| \le k\)

\(1 \le T \le 500,1 \le n \le 10^{18},0 \le k \le 7\)

题解:

首先我们化简一下题意,大致意思就是说,我们每一个点可以选择自己,或者选择自己相邻距离在k以内的点,这两个点就会连接在一起,两个点连接了之后就不能再选了。问把所有点连接有多少种方案。

由于这个n的范围实在是太大了,我们可以考虑矩阵快速幂,但这个也是后话。我们可以先考虑设计一个状态,假设k固定的情况下,则我们对于每一个点 \(i\) 都去考虑和它前面的点相连接。那么考虑前面多少个点好呢?我们只需要考虑前面k个点的情况就好了,而k的范围<=7,完全可以用状压。

用一个s1表示前面k个点的情况。每一位用0表示已经连接了,1表示还没有连接。

设s2是我们操作之后的状态,我们首先分析出来,可以枚举前面s1的每一位,如果这一位是1的话,就可以把这一个1消掉,s2就是消去那一位1的s1再左移1位,因为我们用0表示已经连接,所以s2虽然比s1多了一位,但是那一位是0,所以无伤大雅,本质上还是k位。

同时再讨论一些情况,比如我们还可以s2还可以是s1左移1位,s1左移1位再异或1,有一种特殊情况是当s1>>(k-1)&1==1时,我们就只能连接这一位,不能有别的操作,这样才能保证在前k位之前的所有的点都是被连接的。

大致的思路就是这样。

上述的思路都是在s1左移1位(处理出来1位)的情况下得出来的,把这个矩阵称为g[0],下标是2的0次方;ps:刚才讨论s1->s2,s1便是矩阵的横坐标,s2便是矩阵的纵坐标。

所以我们设一个初始的1(1<<k)的矩阵,去乘以n次这个g0矩阵就可以得到答案了。(此处如果不明白的话可以再复习一下矩阵快速幂)。

但是n是1e18,所以此处我们可以使用矩阵快速幂,g[i]=g[i-1]*g[i-1];

然后n拆成2进制的形式去处理就好了。

以下贴上代码:

#include <bits/stdc++.h>
#define endl '\n'
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rep2(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
void kuaidu() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); }
inline int max(int a, int b) { if (a < b) return b; return a; }
inline int min(int a, int b) { if (a < b) return a; return b; }
typedef pair<int, int> PII;

//=======================================================================
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 998244353;
const int INF = 1e16;
int n, m, T;
Mat f[8][64];
//=======================================================================
signed main() {
	kuaidu();
	for(int k=0;k<=7;k++){
		f[k][0].init(1<<k,1<<(k),0,0);
		rep(s,0,(1<<k)-1){
			if(s>>(k-1)&1){
				f[k][0][s][(s<<1)^(1<<k)]=1;
			}
			else {
				f[k][0][s][(s<<1)|1]=1;
				f[k][0][s][s<<1]=1;
				rep(i,0,k-1-1){
					if(s>>i&1){
						f[k][0][s][(s^(1<<i))<<1]=1;
					}
				}
			}
		}
		rep(i,1,63) {
			f[k][i]=f[k][i-1]*f[k][i-1];
		}
	}
	cin>>T;
	while(T--){
		int k;
		cin>>n>>k;
		Mat ans;
		ans.init(0,1<<k,0,0);
		ans[0][0]=1;//初始化设置
		rep(i,0,63){
			if(n>>i&1) ans=ans*f[k][i];
		}
		cout<<ans[0][0]<<endl;
	}
	return 0;
}

矩阵板子:

struct Mat {
	int a[140][260];
	int n, m, st;
	Mat(){}
	Mat(int n_, int m_, int fl = 0, int st_ = 1) {
		n = n_, m = m_, st = st_;
		if (!fl) rep(i, st, n) rep(j, st, m) a[i][j] = 0;
		if (fl == INF) rep(i, st, n) rep(j, st, m) a[i][j] = INF;
		if (fl == 1) rep(i, st, n) rep(j, st, m) a[i][j] = (i == j);
	}
	void init(int n_, int m_, int fl = 0, int st_ = 1){
		n = n_, m = m_, st = st_;
		if (!fl) rep(i, st, n) rep(j, st, m) a[i][j] = 0;
		if (fl == INF) rep(i, st, n) rep(j, st, m) a[i][j] = INF;
		if (fl == 1) rep(i, st, n) rep(j, st, m) a[i][j] = (i == j);
	}
	int* operator [] (int x) { return a[x]; }
	Mat operator * (Mat b) {
		Mat res(n, b.m, 0, b.st);//记得赋值
		rep(i, st, n) rep(j, st, b.m) rep(k, st, m) {
			res[i][j] += a[i][k] * b[k][j] % mod;
			res[i][j] %= mod;
		}
		return res;
	}
	Mat operator + (Mat b) {
		Mat res(n, b.m, 0, b.st);//记得赋值
		rep(i, st, n) rep(j, st, b.m) {
			res[i][j] = a[i][j] + b[i][j];
			res[i][j] %= mod;
		}
		return res;
	}
	static Mat kuai(Mat A, int b) {
		Mat tem(A.n, A.m, 1, A.st);
		while (b) { if (b % 2) tem = tem * A; b = b / 2, A = A * A; }
		return tem;
	}
	//指数从0开始的求和
	static Mat kuai_sum(Mat A, int b) {
		Mat tem(A.n, A.m, 1, A.st); Mat sum = tem;
		while (b) {
			if (b % 2) tem = sum + tem * A;
			sum = sum + sum * A; A = A * A, b /= 2;
		}
		return tem;
	}
	void out() {
		rep(i, st, n) { rep(j, st, m) cout << a[i][j] << " "; cout << endl; }
	}
};

标签:tem,进阶,int,rep,矩阵,st,杂项,s1,Mat
From: https://www.cnblogs.com/advisedy/p/18417391

相关文章

  • 代码随想录Day2 | LeetCode 209. 长度最小的子数组、LeetCode 59. 螺旋矩阵 II、KamaC
    LeetCode209.长度最小的子数组子数组是一个连续的,很容易想到滑动窗口classSolution:defminSubArrayLen(self,target:int,nums:List[int])->int:windowSum=0left,right=0,0res=float('inf')whileright<len(nums......
  • 【二叉树进阶】二叉搜索树
    目录1.二叉搜索树概念2.二叉搜索树的实现2.1创建二叉搜索树节点2.2创建实现二叉搜索树2.3二叉搜索树的查找2.4 二叉搜索树的插入2.5二叉搜索树的删除2.6中序遍历2.7 完整代码加测试3.二叉搜索树的应用3.1K模型:3.2KV模型:4.二叉搜索树的性能分析1.......
  • 240. 搜索二维矩阵 II
    思路遍历每一行,将当前行的数存放至哈希表中(in的时间复杂度O(1)),查询target是否存在当前行中,是直接返回遍历结束都找不到target,则说明target不存在classSolution(object):defsearchMatrix(self,matrix,target):""":typematrix:List[List[i......
  • Maven笔记(二):进阶使用
    Maven笔记(二)-进阶使用一、Maven分模块开发分模块开发对项目的扩展性强,同时方便其他项目引入相同的功能。将原始模块按照功能拆分成若干个子模块,方便模块间的相互调用,接口共享(类似Jar包一样之间引用、复用)。开发步骤:创建Maven项目书写模块代码分模块开发需要先针对......
  • Docker 进阶篇-CIG 重量级监控系统
    上一篇讲的是轻量级的监控工具,本文就来讲重量级的:CAdvisor+InfluxDB+Granfana,简称CIG。​‍‍dockerstats原生的Docker命令中,stats可以查看每个容器占用的CPU,内存,网络流量等情况:CONTAINERIDNAMECPU%MEMUSAGE/LIMITMEM%NETI/O......
  • 59. 螺旋矩阵 II
    不知道一年后会成长成什么样,只感觉好难好难。有好多东西要学,源码也看不懂,项目也不会做。classSolution{public:vector<vector<int>>generateMatrix(intn){vector<vector<int>>vec(n,vector<int>(n,0));intnum=1;for(inti=0;i......
  • c++入门(七万字心得体会!!)分上下两篇(初阶+进阶)
    目录c++入门c++关键字命名空间命名空间定义命名空间使用c++输入输出缺省参数缺省参数概念缺省参数分类函数重载函数重载概念c++支持函数重载原理--名字修饰(name)引用引用概念引用特性常引用使用场景传值,传引用效率对比引用和指针的区别内联函数概念特性a......
  • C语言学习进阶路线图
    目录一、基础准备1.1.了解计算机基础知识1.2.安装开发环境二、入门学习2.1.学习C语言基本语法2.2.编写简单程序三、进阶概念3.1.函数与模块3.2.数组与字符串3.3.指针基础四、深入探索4.1.指针高级应用4.2.结构体与联合体4.3.文件操作五、高级特性5.1......
  • 【C++】模板进阶:深入解析模板特化
    C++语法相关知识点可以通过点击以下链接进行学习一起加油!命名空间缺省参数与函数重载C++相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C++内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与Queue本章将......
  • C++数据结构-二叉树的三种遍历方法(进阶篇)
    1.遍历简介:树作为非线性数据结构,在我们取出数据时就需要设计遍历,所谓遍历,就是按照一定的规则性,将数据结构中的所有数据全部依次访问,而二叉树本身并不具有天然的全局次序,故为实现遍历,需通过在各节点与其孩子之间约定某种局部次序,间接地定义某种全局次序,这便是我们常规定的先序......