题解 CF226A Flying Saucer Segments
思路分析
这题一看到感觉它就是汉诺塔啊!但是它和汉诺塔的不同之处在于:它每次只能相邻移动。一时间想不出思路怎么办!先看样例解释。
我们可以发现以下两点性质:
-
在船员 \(t\) 要从飞船 \(3\) 转移到飞船 \(1\) 时:
- 飞船 \(1\) 中只有且全是编导从 \(t + 1\) 到 \(n\) 的船员;
- 飞船 \(2\) 为空;
- 飞船 \(3\) 中只有且全是编号从 \(1\) 到 \(t\) 的船员。
-
将船员 \(t\) 从飞船 \(3\) 转移到飞船 \(1\) 时,依次进行如下操作:
- 将船员 \(t\) 从飞船 \(3\) 转移到飞船 \(2\);
- 将船员 \(t+1\) 到 \(n\) 从飞船 \(1\) 转移到飞船 \(3\);
- 将船员 \(t\) 从飞船 \(2\) 转移到飞船 \(1\);
- 将船员 \(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}\).
希望能对您有所帮助。

浙公网安备 33010602011771号