P6148 [USACO20FEB] Swapity Swapity Swap S

P6148 [USACO20FEB] Swapity Swapity Swap S

Farmer John 的 N 头奶牛(1N105)站成一排。对于每一个 1iN,从左往右数第 i 头奶牛的编号为 i

Farmer John 想到了一个新的奶牛晨练方案。他给奶牛们 M 对整数 (L1,R1)(LM,RM),其中 1M100。他让她们重复以下包含 M 个步骤的过程 K1K109)次:

对于从 1M 的每一个 i

  • 当前从左往右数在位置 LiRi 的奶牛序列反转她们的顺序。
  • 当奶牛们重复这一过程 K 次后,请对每一个 1iN 输出从左往右数第 i 头奶牛的编号。

思路1:

首先看暴力,暴力 O(nmk),非常的去世。

首先我们可以自然而然的想到用矩阵代表每一次变换(即代表一次(Li,Ri))的交换。

初始答案矩阵:

[123n]

那么对于一次交换 (Li,Ri) 的转移,我们可以构造出转移矩阵:

[100...0010...0001...0([Li,Ri])000...1]

其中 [Li,Ri] 的子矩阵是:

[00...0100...1001...0010...00]

其中副斜对角线上的的值全为 1.

转移矩阵表示的是其他元素不变,(Li,Ri) 之间的元素全部交换.

那么我们只需要对 m 个操作的矩阵全部乘起来,然后自乘 k 次,最后在与答案矩阵相乘即可.

但是我们发现 n1e5,也就是说我们的转移矩阵的大小要是 1e5×1e5 的大小,不 TLE 也得 MLE.

我们发现,转移矩阵的每一行,每一列都只对应着一个 1.

则我们可以把这个矩阵压缩成 1×1e5 的矩阵:

[123RiRi1Li+1LiRi+1n]

i 行的元素代表了原转移矩阵第 i 行中的 1 在第几列.

那么我们转移矩阵的相乘就可以变成: (其中 ans 是答案矩阵,a,b 是转移矩阵.)

a×b:ansi=bai

矩阵的自乘就会变为:

a2:ansi=aai

这样看就很简单了,我们定义 MATRIX 结构体,在里面重载运算符实现 × 运算即可.

code:

#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 1e5 + 7;
const int MAXM = 105;
int n,m,k;
struct OPERATE{
	int l,r;
}op[MAXM];
struct MATRIX{
	int a[MAXN];
	void init(){ for(int i = 1;i <= n;i++) a[i] = i; }
	MATRIX operator*(const MATRIX &other){
		MATRIX ans;
		int res[MAXN];
		for(int i = 1;i <= n;i++) res[i] = other.a[i];
		for(int i = 1;i <= n;i++) ans.a[i] = res[a[i]];
		return ans;
	}
	void print_ans(){ for(int i = 1;i <= n;i++) cout << a[i] << endl; }
}op_matrix[MAXM];
MATRIX anss;
MATRIX qpow(MATRIX a,int x){
	MATRIX res;
	res.init();
	while(x){
		if(x & 1) res = a * res;
		a = a * a;
		x >>= 1;
	}	
	return res;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n >> m >> k;
	for(int i = 1;i <= m;i++){
		cin >> op[i].l >> op[i].r;
		for(int j = op[i].l;j <= op[i].r;j++) op_matrix[i].a[op[i].r - (j - op[i].l)] = j;
		for(int j = 1;j <= n;j++) if(op_matrix[i].a[j] == 0) op_matrix[i].a[j] = j;
	}
	anss.init();
	for(int i = 1;i <= m;i++) anss = op_matrix[i] * anss;
	anss = qpow(anss,k);
	anss.print_ans();
	return 0;
}

思路2

我们发现,对于一次反转后,每个位置上对应的数是独一无二的.

我们发现这跟函数的定义很像,即定义域上的每一个值都对应了唯一的一个值.

对于每 i 次操作,我们设为 fi 为一次映射,则对于一次操作序列的映射效果 g,我们有:

g(E)=f1(f2(...fm(E)))

其中 E 表示初始状态.

我们用 [1,2,...,(Ri,Ri1,...,Li),Ri+1,...,n] 来表示一次操作序列中的一次操作,那么我们只需要把这 m 个映射合在一起即可.

然后用倍增的思想来处理 k 次操作序列.

代码与上面非常的相似.

posted @   wyl123ly  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2023-10-14 string用法合集
点击右上角即可分享
微信分享提示