POJ3735 Training little cats

我们看到这个题,很容易想到矩阵快速幂的做法。因为题目中两个条件实在太显眼:

  1. All the cats perform a sequence of these moves and must repeat it \(m\) times!
  2. \(m\leq 10^{10}\)

一个相同的操作,做 \(10^{10}\) 次,肯定是矩阵快速幂,问题就是我们如何把三个操作转化为 \((n+1)\times (n+1)\) 的矩阵 \(opt\)\(opt\) 一开始是一个单位矩阵。

首先我们先确定一个 \(ans\) 矩阵,\(ans_{1,i}\) 表示第 \(i\) 只猫的花生数。联想到花生米@花少北。 那么一开始矩阵就是长这样:

\[\begin{bmatrix}0&\cdots&0\end{bmatrix} \]

我们先来分析三个操作:

  • g x。给第 \(x\) 只猫加一颗花生,我们可以采用一个小技巧:扩充矩阵。我们先把 \(ans\) 矩阵变化一下,变成 \(n\)\(0\)\(1\)\(1\),如下:

\[\begin{bmatrix}0&\cdots&0\ 1\end{bmatrix} \]

那么我们只需要给 \(opt_{n+1,x}\) 加上一个 \(1\),就可以满足这个要求。因为在乘法是会有 \(ans_{i,x}+=ans_{1,n+1}\times opt_{n+1,x}\)

  • e x。这个操作最简单。我们直接清空 \(opt\) 中第 \(x\) 列即可。

  • s x y。交换操作也很简单,也就是把 \(opt\)\(x,y\) 两列交换一下即可。

最后快速幂计算即可。

注意一下本题中这是一个稀疏矩阵,我们可以用一个小优化来通过老爷机的评测。 详情见代码注释。

//Don't act like a loser.
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

const int MAXN=110;

int n,m,k;

struct matrix {
	long long data[MAXN][MAXN];
	int col,row;
	
	void clear() {
		col=row=0;
		for(int i=1;i<=n+1;i++) {
			for(int j=1;j<=n+1;j++) {
				data[i][j]=0;
			}
		}
	}
	void initialize() {
		clear();
		col=row=n+1;
		for(int i=1;i<=n+1;i++) {
			data[i][i]=1;
		}
	}
	
	inline matrix operator *(matrix& b)const {
		matrix ans;
		ans.clear();
		
		for(int i=1;i<=col;i++) {
			for(int k=1;k<=row;k++) {
				if(data[i][k]==0) continue;//这里有一个小优化~
				for(int j=1;j<=b.row;j++) {
					ans.data[i][j]+=data[i][k]*b.data[k][j];
				}
			}
		}
		ans.col=col;
		ans.row=b.row;
		return ans;
	}
};

matrix ans;
matrix opt;

inline void qpow(int y) {
	while(y) {
		if(y&1) {
			ans=ans*opt;
		}
		opt=opt*opt;
		y>>=1;
	}
}

signed main() {
	while(1) {
		scanf("%d%d%d",&n,&m,&k);
		if(n==0&&m==0&&k==0) break;

		ans.clear();
		ans.col=1;
		ans.row=n+1;
		for(int i=1;i<=n;i++) {
			ans.data[1][i]=0;
		}
		ans.data[1][n+1]=1;
		
		opt.initialize();
		for(int i=1;i<=k;i++) {
			char ch[5];
			int x,y;
			
			scanf("%s",ch);
			if(ch[0]=='s')
				scanf("%d%d",&x,&y);
			else 
				scanf("%d",&x);
			
			if(ch[0]=='s') {
				for (int j=1;j<=n+1;++j)
					swap(opt.data[j][x],opt.data[j][y]);
			}
			if(ch[0]=='g') {
				opt.data[n+1][x]++;
			}
			if(ch[0]=='e') {
				for (int j=1; j<=n+1;++j)
					opt.data[j][x]=0;
			}
		}
		
		qpow(m);
		
		for(int i=1;i<=n;i++) {
			printf("%lld ",ans.data[1][i]);
		}
		printf("\n");
	}
	return 0;
}

原文地址:https://www.cnblogs.com/huayucaiji/p/POJ3735.html