题解 CF226A Flying Saucer Segments

传送门

思路分析

这题一看到感觉它就是汉诺塔啊!但是它和汉诺塔的不同之处在于:它每次只能相邻移动。一时间想不出思路怎么办!先看样例解释。

我们可以发现以下两点性质:

  • 在船员 \(t\) 要从飞船 \(3\) 转移到飞船 \(1\) 时:

    • 飞船 \(1\) 中只有且全是编导从 \(t + 1\)\(n\) 的船员;
    • 飞船 \(2\) 为空;
    • 飞船 \(3\) 中只有且全是编号从 \(1\)\(t\) 的船员。
  • 将船员 \(t\) 从飞船 \(3\) 转移到飞船 \(1\) 时,依次进行如下操作:

    1. 将船员 \(t\) 从飞船 \(3\) 转移到飞船 \(2\)
    2. 将船员 \(t+1\)\(n\) 从飞船 \(1\) 转移到飞船 \(3\)
    3. 将船员 \(t\) 从飞船 \(2\) 转移到飞船 \(1\)
    4. 将船员 \(t+1\)\(n\) 从飞船 \(3\) 转移到飞船 \(1\)

再加上我们自己看题目可以得出的以下性质:

  • 将任意船员全部从飞船 \(1\) 转移到飞船 \(3\) 所花费的时间与将其全部从飞船 \(3\) 转移到飞船 \(1\) 所花费的时间是想等的;
  • 转移船员所需的世界只与船员数量有关,与编号无关。

我们便可以想到以下方法:

\(dp_i\) 表示将 \(n-i+1\) 号船员从飞船 \(1\) 转移到飞船 \(3\) 所需的时间,\(sum_i\) 表示将 \(i\) 个船员全部从飞船 \(3\) 转移到飞船 \(1\) 所需要的时间。

结合对样例解释的分析,我们很容易便可以写出:

\[dp_i=sum_{i-1}\times 2+2 \]

\[sum_i=sum_{i-1}+dp_i \]

由于题目所求的答案即为 \(sum_n\),我们可以将以上两个式子化简一下:

\[sum_i=sum_{i-1}\times 3+3 \]

因此,我们只要通过以上式子求出答案即可。

方法解析

显然,\(sum_1=2\)
考虑到 \(n\le 10^9\) 的数据范围与如此表达的解析式,我们可以很快想到矩阵快速幂。

直接上代码。

AC 代码

此代码来自于 @ zc_Ethan 大佬,在此特别感谢其帮助(因为本蒟蒻不会写矩阵快速幂……)。

#include<bits/stdc++.h>

#define ll long long
#define inf 1 << 30
#define INF 1ll << 60

using namespace std;
const int siz = 2;
ll MOD;

struct Node
{
    ll a[siz][siz];
};

Node mul(Node m1, Node m2)
{
	Node t;
	memset(t.a, 0, sizeof(t.a));
	for(int i=0; i < siz; i ++)
    {
		for(int k = 0; k < siz; k ++)
        {
			if(!m1.a[i][k]) continue;
			for(int j = 0; j < siz; j ++)
            {
                t.a[i][j] = (t.a[i][j] + m1.a[i][k] * m2.a[k][j] % MOD) % MOD;
            }
		}
	}return t;
}

Node Work(Node m, ll p)
{
	Node t;
	memset(t.a, 0, sizeof(t.a));
	for(int i = 0; i < siz; i ++) t.a[i][i] = 1;
	while(p)
    {
		if(p & 1) t = mul(t, m);
		m = mul(m, m); p >>= 1;
	}
    return t;
}

int main()
{
	ll n;
	scanf("%lld %lld", &n, &MOD);

	if(n == 1)
    {
		printf("%lld\n", 2ll % MOD);
		return 0;
	}

	Node sum, op;

	sum.a[0][0] = 2;
	sum.a[1][0] = 1;
	op.a[0][0] = 3; op.a[0][1] = 2;
	op.a[1][0] = 0; op.a[1][1] = 1;
	op = Work(op, n - 1);
	sum = mul(op, sum);

	printf("%lld\n", sum.a[0][0] % MOD);
}

Written by \(\operatorname{Rosmarinus}\).

希望能对您有所帮助。

posted @ 2021-04-18 15:37  XJ21  阅读(64)  评论(0)    收藏  举报