2020牛客寒假算法基础集训营1(2020.2.4)
比赛链接:https://ac.nowcoder.com/acm/contest/3002
Accepted
- A - honoka和格点三角形
- B - kotori和bangdream
- C - umi和弓道
- D - hanayo和米饭
- E - rin和快速迭代
- F - maki和tree
- G - eli和字符串
- H - nozomi和字符串
- I - nico和niconiconi
- J - u’s的影响力
A-honoka和格点三角形
题目描述
honoka最近在研究三角形计数问题。
她认为,满足以下三个条件的三角形是“好三角形”。
1.三角形的三个顶点均为格点,即横坐标和纵坐标均为整数。
2.三角形的面积为。
3.三角形至少有一条边和
x
x
x 轴或
y
y
y 轴平行。
honoka想知道,在平面中选取一个大小为
n
∗
m
n*m
n∗m 的矩形格点阵,可以找到多少个不同的“好三角形”?
由于答案可能过大,请对
1000000007
1000000007
1000000007 取模。
输入描述:
两个正整数和(
2
≤
n
,
m
≤
1
0
9
2 \le n,m \le 10^9
2≤n,m≤109 )
输出描述:
面积为1的格点三角形的数量,对
1
e
9
+
7
1e9+7
1e9+7 取模的结果。
解题思路:
分为底为1高为2,底为2高为1,每种情况又可分成底平行于
x
x
x 轴或者底平行于
y
y
y 轴。
a
n
s
=
(
m
−
2
)
∗
m
∗
(
n
−
1
)
∗
2
+
(
n
−
2
)
∗
n
∗
(
m
−
1
)
∗
2
+
(
n
−
1
)
∗
(
m
−
2
)
∗
(
n
−
2
)
∗
2
+
(
m
−
1
)
∗
(
n
−
2
)
∗
(
m
−
2
)
∗
2
ans = (m-2)*m*(n-1)*2 + (n-2)*n*(m-1)*2 + (n-1)*(m-2)*(n-2)*2 + (m-1)*(n-2)*(m-2)*2
ans=(m−2)∗m∗(n−1)∗2+(n−2)∗n∗(m−1)∗2+(n−1)∗(m−2)∗(n−2)∗2+(m−1)∗(n−2)∗(m−2)∗2 ,每一步取模。注意不能重复。
Code:
#include <iostream>
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;
typedef long long ll;
const int MOD = 1000000007;
ll M(ll x, ll y) { return ((x % MOD) * (y % MOD)) % MOD; }
int main()
{
IO;
ll n, m;
cin >> n >> m;
ll ans = 0;
ans = (M(M(m - 2, m), M(2, n - 1)) + M(M(n - 2, n), M(2, m - 1)) + M(M(n - 1, m - 2), M(n - 2, 2)) + M(M(m - 1, n - 2), M(m - 2, 2))) % MOD;
cout << ans << endl;
return 0;
}
/*
示例1
2 3
6
示例2
100 100
7683984
*/
B-kotori和bangdream
题目描述
有一天,kotori发现了一个和lovelive相似的游戏:bangdream。令她惊讶的是,这个游戏和lovelive居然是同一个公司出的!
kotori经过一段时间的练习后已经变得非常触,每个音符
x
%
x\%
x% 的概率perfect,获得
a
a
a 分,
(
100
−
x
)
%
(100−x)\%
(100−x)% 概率great,获得
b
b
b 分。
已知一首歌有
n
n
n 个音符。kotori想知道,不考虑连击加成的话,一首歌得分的期望是多少?
输入描述:
一行
4
4
4 个整数,用空格隔开。分别是
n
,
x
,
a
,
b
n,x,a,b
n,x,a,b 。
(
0
≤
x
≤
100
,
1
≤
n
,
a
,
b
≤
1000
0 \le x \le 100,1 \le n,a,b \le 1000
0≤x≤100,1≤n,a,b≤1000 )
输出描述:
一首歌得分的期望,保留两位小数。
输入
100 50 500 400
输出
45000.00
说明
如果全perfect是50000分,全great是40000分。由于它们的概率都是50%,即perfect和great五五开,所以期望是45000分。
解题思路:
这是一个简单的求数学期望的问题。
Code:
#include <cstdio>
int main() {
int n, x, a, b;
scanf("%d%d%d%d", &n, &x, &a, &b);
printf("%.2f\n", (a * n * x + b * n * (100 - x)) / 100.0);
return 0;
}
D-hanayo和米饭
题目描述
hanayo很喜欢吃米饭。
有一天,她拿出了
n
n
n 个碗,第一个碗装了
1
1
1 粒米饭,第二个碗装了
2
2
2 粒米饭,以此类推,第
n
n
n 个碗装了
n
n
n 粒米饭。
然而,爱搞恶作剧的rin把所有的碗的顺序打乱,并拿走了一个碗。hanayo想知道,rin拿走的碗里有多少粒米饭?
输入描述:
第一行输入一个正整数
n
n
n 。代表原始的总碗数。
第二行输入
n
n
n 个正整数
a
i
a_i
ai ,代表目前每碗里米饭数量。
保证输入合法。
(
2
≤
n
≤
100000
,
1
≤
a
i
≤
n
2 \le n \le 100000,1 \le a_i \le n
2≤n≤100000,1≤ai≤n)
输出描述:
输出一个正整数,代表rin拿走的碗里米饭数量。
解题思路:
用数组a标记,再遍历一遍数组a找未被标记的输出下标。
Code:
#include <iostream>
#include <cstring>
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;
int n;
bool a[100002];
int main() {
IO;
cin >> n;
int x;
memset(a, false, sizeof(a));
for (int i = 1; i <= n; i++) {
cin >> x;
a[x] = true;
}
for (int i = 1; i <= n; i++) {
if (!a[i]) {
cout << i << endl;
return 0;
}
}
return 0;
}
/*
示例
5
2 5 1 3
4
*/
E-rin和快速迭代
题目描述
rin最近喜欢上了数论。
然而数论实在太复杂了,她只能研究一些简单的问题。
这天,她在研究正整数因子个数的时候,想到了一个“快速迭代”算法。设
f
(
x
)
f(x)
f(x) 为
x
x
x 的因子个数,将
f
f
f 迭代下去,rin猜想任意正整数最终都会变成
2
2
2 。
例如:
f
(
12
)
=
6
,
f
(
6
)
=
4
,
f
(
4
)
=
3
,
f
(
3
)
=
2
f(12)=6,f(6)=4,f(4)=3,f(3)=2
f(12)=6,f(6)=4,f(4)=3,f(3)=2 。
她希望你帮她验证一下。她会给你一个正整数 ,让你输出它在迭代过程中,第一次迭代成
2
2
2 的迭代次数。
输入描述:
一个正整数
n
n
n(
3
≤
n
≤
1
0
12
3 \le n \le 10^{12}
3≤n≤1012)
输出描述:
一个正整数,为
n
n
n 迭代至
2
2
2 的次数。
解题思路:
暴力求解。
Code:
#include <iostream>
#include <algorithm>
#include <cmath>
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;
typedef long long ll;
ll get(ll x) {
ll sum = 0, sq = sqrt(x); //开方,降低复杂度
for (ll i = 1; i <= sq; i++) {
if (x % i == 0) {
sum += 2;
}
}
if (sq * sq == x) {
sum--;
}
return sum;
}
int main() {
IO;
ll n, ans = 0;
cin >> n;
for (;;) {
ans++;
if ((n = get(n)) == 2) break;
}
cout << ans << endl;
return 0;
}
/*
示例1
12
4
*/
F-maki和tree
题目描述
有一天,maki拿到了一颗树。所谓树,即没有自环、重边和回路的无向连通图。
这个树有
n
n
n 个顶点,
n
−
1
n-1
n−1 条边。每个顶点被染成了白色或者黑色。
maki想知道,取两个不同的点,它们的简单路径上有且仅有一个黑色点的取法有多少?
注:
①树上两点简单路径指连接两点的最短路。
②
<
p
,
q
>
<p,q>
<p,q> 和
<
q
,
p
>
<q,p>
<q,p> 的取法视为同一种。
输入描述:
第一行一个正整数
n
n
n 。代表顶点数量。(
1
≤
n
≤
100000
1 \le n \le 100000
1≤n≤100000)
第二行是一个仅由字符 ‘B’ 和 ‘W’ 组成的字符串。第
i
i
i 个字符是 B 代表第
i
i
i 个点是黑色, W 代表第
i
i
i 个点是白色。
接下来的
n
−
1
n-1
n−1 行,每行两个正整数
x
x
x ,
y
y
y ,代表
x
x
x 点和
y
y
y 点有一条边相连 (
1
≤
x
,
y
≤
n
1 \le x,y \le n
1≤x,y≤n)
输出描述:
一个正整数,表示只经过一个黑色点的路径数量。
样例输入
3
WBW
1 2
2 3
样例输出
3
说明
树表示如下:
其中只有2号是黑色点。
<1,2>、<2,3>、<1,3>三种取法都只经过一个黑色点。
解题思路:
并查集预处理白色连通块,统计每个白色连通块内点的数量。
一条路径中只经过一个黑点,我们可以先确定黑点,再确定路径。
经过一个黑点的路径有两种:两个端点都是白点;其中一个端点是黑点。
树中两个点之间的路径只有一条,只要两个端点不完全相同,那么这条路径就不相同。所以我们可以选黑点相邻的白色连通块中的任意一个点(即白色连通块的大小)。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100002;
int n;
char str[N];
vector<int> e[N];
int fa[N], cnt[N];
int findd(int x)
{
if (fa[x] == x)
return x;
else
{
fa[x] = findd(fa[x]);
return fa[x];
}
}
void mergee(int x, int y)
{
int fx = findd(x), fy = findd(y);
if (fx != fy)
{
fa[fy] = fx;
cnt[fx] += cnt[fy];
}
}
int main()
{
scanf("%d", &n);
scanf("%s", str + 1);
int u, v;
for (int i = 1; i <= n; i++)
{
fa[i] = i;
cnt[i] = 1;
}
for (int i = 0; i < n - 1; i++)
{
scanf("%d%d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
if (str[u] == 'W' && str[v] == 'W')
mergee(u, v);
}
ll ans = 0;
for (int i = 1; i <= n; i++)
{
if (str[i] == 'B')
{
ll sum = 0;
for (int j = 0; j < e[i].size(); j++)
{
v = e[i][j];
if (str[v] == 'W')
{
int t = findd(v);
ans += cnt[t] + sum * cnt[t];
sum += cnt[t];
}
}
}
}
printf("%lld\n", ans);
return 0;
}
G-eli和字符串
题目描述
eli拿到了一个仅由小写字母组成的字符串。
她想截取一段连续子串,这个子串包含至少
k
k
k 个相同的某个字母。
她想知道,子串的长度最小值是多少?
注:所谓连续子串,指字符串删除头部和尾部的部分字符(也可以不删除)剩下的字符串。例如:对于字符串“arcaea”而言,“arc”、“rcae”都是其子串。而“car”、“aa”则不是它的子串。
输入描述:
第一行输入两个正整数
n
n
n 和
k
k
k(
1
≤
k
≤
n
≤
200000
1 \le k \le n \le 200000
1≤k≤n≤200000)
输入仅有一行,为一个长度为
n
n
n 的、仅由小写字母组成的字符串。
输出描述:
如果无论怎么取都无法满足条件,输出
−
1
-1
−1 。
否则输出一个正整数,为满足条件的子串长度最小值。
解题思路:
分别找26个字母,每次先记录下字母ch
的所有位置,然后再用记录下的位置更新最小值。
Code:
#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
int n, k;
string s;
int len, book[200002];
int main() {
IO;
cin >> n >> k;
cin >> s;
len = s.length();
int ans = INF;
for (int i = 0; i < 26; i++) {
char ch = 'a' + i;
int cnt = 0;
memset(book, 0, sizeof(book)); //清0
for (int j = 0; j < len; j++) { //记录ch的所有位置
if (s[j] == ch) {
book[cnt++] = j;
}
}
if (cnt < k) continue; //如果ch不够k个
for (int j = 0; j <= cnt - k; j++) { //连续的k个为一个子串,更新最小值
ans = min(ans, book[j + k - 1] - book[j] + 1);
}
}
if (ans < INF) cout << ans << endl;
else cout << -1 << endl;
return 0;
}
/*
示例1
5 2
abeba
3
说明:选择“beb”子串,长度为3,其中包含相同的两个'b'
*/
H-nozomi和字符串
题目描述
nozomi看到eli在字符串的“花园”里迷路了,决定也去研究字符串问题。
她想到了这样一个问题:
对于一个“01”串而言,每次操作可以把 0 字符改为 1 字符,或者把 1 字符改为 0 字符。所谓“01”串,即只含字符 0 和字符 1 的字符串。
nozomi有最多
k
k
k 次操作的机会。她想在操作之后找出一个尽可能长的连续子串,这个子串上的所有字符都相同。
nozomi想问问聪明的你,这个子串的长度最大值是多少?
注:
k
k
k 次操作机会可以不全部用完。
如果想知道连续子串的说明,可以去问问eli,nozomi不想再讲一遍。
输入描述:
第一行输入两个正整数
n
n
n 和
k
k
k(
1
≤
k
≤
n
≤
200000
1 \le k \le n \le 200000
1≤k≤n≤200000)
输入仅有一行,为一个长度为
n
n
n 的、仅由字符
0
0
0 和
1
1
1 组成的字符串。
输出描述:
一个正整数,为满足条件的子串长度最大值。
解题思路:
题目要求最多改
k
k
k 次,这
k
k
k 次肯定是全都改
0
0
0 或者全都改
1
1
1 ,答案就是由某次更改连续
k
k
k 个
0
0
0 或
1
1
1 得来的。所以只要求出所有的连续
k
k
k 个
0
0
0 和连续
k
k
k 个
1
1
1 区间的长度,取最大值即可。
用两个数组a
、b
分别记录
0
0
0 和
1
1
1 的位置,以
k
k
k 长度为间隔分别遍历数组a
、b
。以遍历a
数组为例(修改
0
0
0),
a
n
s
=
m
a
x
(
a
n
s
,
a
[
j
+
1
]
−
a
[
i
−
1
]
−
1
)
,
其
中
j
−
i
+
1
=
k
ans = max(ans, a[j + 1] - a[i - 1] - 1),其中j-i+1=k
ans=max(ans,a[j+1]−a[i−1]−1),其中j−i+1=k,意思就是修改第
i
i
i 个
0
0
0 到第
j
j
j 个
0
0
0 ,一共修改
k
k
k 个
0
0
0,修改后连续相同1的长度就是第
i
−
1
i-1
i−1 个
0
0
0 到第
j
+
1
j+1
j+1 个
0
0
0 之间的距离。
这里有个小问题,要在开头和结尾加上两个辅助点
−
1
-1
−1 和
n
n
n 。设想,如果
j
j
j 是最后一个
0
0
0 ,那么他就没有
j
+
1
j+1
j+1 个点来求修改后的距离,此时修改后的距离是到字符串结尾的距离,所以要在数组最后加上
n
n
n ,同理开头一样。
Code:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;
const int INF = 0x3f3f3f3f;
int n, k;
string s;
vector<int> a, b;
int ans;
int main() {
IO;
cin >> n >> k >> s;
a.push_back(-1);
b.push_back(-1); //多一个
for (int i = 0; i < n; i++) {
if (s[i] == '0') a.push_back(i);
else b.push_back(i);
}
a.push_back(n);
b.push_back(n); //多两个
if (a.size() - 2 <= k) ans = n;
else { //0大于k个
for (int i = 1, j = k; j < a.size() - 1; i++, j++) {
ans = max(ans, a[j + 1] - a[i - 1] - 1);
}
}
if (b.size() - 2 <= k) ans = n; //前面多两个,减2
else { //1大于k个
for (int i = 1, j = k; j < b.size() - 1; i++, j++) {
ans = max(ans, b[j + 1] - b[i - 1] - 1);
}
}
cout << ans << endl;
return 0;
}
/*
示例
5 1
10101
3
*/
I-nico和niconiconi
题目描述
nico平时最喜欢说的口头禅是niconiconi~。
有一天nico在逛著名弹幕网站"niconico"的时候惊异的发现,n站上居然有很多她的鬼畜视频。其中有一个名为《让nico为你洗脑》的视频吸引了她的注意。
她点进去一看,就被洗脑了:“niconicoh0niconico*^vvniconicoG(vniconiconiconiconiconicoG(vniconico…”
弹幕中刚开始有很多“nico1 nico2”等计数菌,但到后面基本上都是“计数菌阵亡”的弹幕了。
nico也想当一回计数菌。她认为:“nico” 计
a
a
a 分,“niconi” 计
b
b
b 分,“niconiconi” 计
c
c
c 分。
她拿到了一个长度为
n
n
n 的字符串,请帮她算出最大计数分数。
注:已被计数过的字符不能重复计数!如"niconico"要么当作"nico"+“nico"计
2
a
2a
2a 分,要么当作"niconi”+"co"计
b
b
b 分。
输入描述:
第一行四个正整数
n
,
a
,
b
,
c
n,a,b,c
n,a,b,c 。(
1
≤
n
≤
300000
,
1
≤
a
,
b
,
c
≤
1
0
9
1 \le n \le 300000,1 \le a,b,c \le 10^9
1≤n≤300000,1≤a,b,c≤109)
第二行是一个长度为
n
n
n 的字符串。
输出描述:
一个整数,代表最大的计数分数。
解题思路:
动态规划。
d
p
[
i
]
dp[i]
dp[i] 代表前
i
i
i 个字符的计数最大值,状态转移方程见代码。
直接看代码吧,比较好理解。
Code:
#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;
typedef long long ll;
ll n, a, b, c, f[300002];
string str;
int main()
{
IO;
cin >> n >> a >> b >> c;
cin >> str;
for (int i = 0; i < n; ++i)
{
if (i != 0)
f[i] = f[i - 1];
if (i >= 3 && str.substr(i - 3, 4) == "nico")
f[i] = max(f[i], f[i - 3] + a);
if (i >= 5 && str.substr(i - 5, 6) == "niconi")
f[i] = max(f[i], f[i - 5] + b);
if (i >= 9 && str.substr(i - 9, 10) == "niconiconi")
f[i] = max(f[i], f[i - 9] + c);
}
cout << f[n - 1] << endl;
return 0;
}
/*
输入
19 1 2 5
niconiconiconiconi~
输出
7
*/
J-u’s的影响力
题目描述
本题测试样例已经更(jia)新(qiang)。
μ’s在九人齐心协力下,影响力越来越大了!
已知第一天影响力为
x
x
x ,第二天影响力为
y
y
y ,从第三天开始,每一天的影响力为前两天影响力的乘积再乘以
a
a
a 的
b
b
b 次方。 用数学语言描述是:
设第
i
i
i 天的影响力为
f
(
i
)
f(i)
f(i) ,那么
f
(
1
)
=
x
,
f
(
2
)
=
y
f(1)=x,f(2)=y
f(1)=x,f(2)=y,对于
i
>
2
i>2
i>2 ,
f
(
i
)
=
f
(
i
−
1
)
∗
f
(
i
−
2
)
∗
a
b
f(i)=f(i-1)*f(i-2)*a^b
f(i)=f(i−1)∗f(i−2)∗ab
她们想知道第
n
n
n 天影响力是多少?
由于这个数可能非常大,只需要输出其对
1000000007
1000000007
1000000007 取模的值就可以了。
输入描述:
一行五个正整数: 。
输出描述:
第 天的影响力对 取模的值。
输入
4 2 3 2 1
输出
72
说明
f
(
1
)
=
2
,
f
(
2
)
=
3
,
f
(
3
)
=
f
(
1
)
∗
f
(
2
)
∗
2
=
12
,
f
(
4
)
=
f
(
2
)
∗
f
(
3
)
∗
2
=
72
f(1)=2,f(2)=3,f(3)=f(1)*f(2)*2=12,f(4)=f(2)*f(3)*2=72
f(1)=2,f(2)=3,f(3)=f(1)∗f(2)∗2=12,f(4)=f(2)∗f(3)∗2=72
备注:
1000000007
1000000007
1000000007 是素数。
解题思路:
数论,矩阵快速幂。
显然
f
(
n
)
f(n)
f(n) 可以用
x
x
x ,
y
y
y 和
a
a
a 这三个因子表达出来。
每一项
a
a
a 的幂次分别是
0
、
0
、
b
、
2
b
、
4
b
、
7
b
、
12
b
…
…
0、0、b、2b、4b、7b、12b……
0、0、b、2b、4b、7b、12b…… 从第三项开始每一项为前两项之和加
1
1
1 。
而
x
x
x 和
y
y
y 的幂次构成斐波那契数列:
x
x
x 的幂次第一项是
1
1
1 ,第二项是
0
0
0 ;
y
y
y 的幂次第一项是
0
0
0 ,第二项是
1
1
1 。之后每一项均为前两项之和。
根据费马小定理,由于
1
e
9
+
7
1e9+7
1e9+7 是素数,有
a
1
e
9
+
6
≡
1
(
m
o
d
1
e
9
+
7
)
a^{1e9+6 }≡1(mod 1e9+7)
a1e9+6 ≡1(mod 1e9+7) 。因此幂的指数对
1
e
9
+
6
1e9+6
1e9+6 取模即可。要注意
a
a
a 是
1000000007
1000000007
1000000007 的倍数的特殊情况。
可以用矩阵快速幂
O
(
l
o
g
n
)
O(logn)
O(logn) 求出
x
、
y
、
a
x、y、a
x、y、a 幂次的第
n
n
n 项。
矩阵快速幂求斐波那契数列的第
n
n
n 项方法如下:
假设
f
(
i
)
f(i)
f(i) 代表斐波那契数列的第
i
i
i 项,显然有:
[
0
1
1
1
]
∗
[
f
(
i
)
f
(
i
+
1
)
]
=
[
f
(
i
+
1
)
f
(
i
)
+
f
(
i
+
1
)
]
=
[
f
(
i
+
1
)
f
(
i
+
2
)
]
\begin{bmatrix} 0 & 1\\ 1 & 1 \end{bmatrix} * \begin{bmatrix} f(i)\\ f(i+1) \end{bmatrix} =\begin{bmatrix} f(i+1)\\ f(i)+f(i+1) \end{bmatrix} =\begin{bmatrix} f(i+1)\\ f(i+2) \end{bmatrix}
[0111]∗[f(i)f(i+1)]=[f(i+1)f(i)+f(i+1)]=[f(i+1)f(i+2)]
因此
[
0
1
1
1
]
n
∗
[
f
(
1
)
f
(
2
)
]
=
[
f
(
n
+
1
)
f
(
n
+
2
)
]
\begin{bmatrix} 0 &1\\ 1 &1 \end{bmatrix}^n * \begin{bmatrix} f(1)\\ f(2) \end{bmatrix} = \begin{bmatrix} f(n+1)\\ f(n+2) \end{bmatrix}
[0111]n∗[f(1)f(2)]=[f(n+1)f(n+2)]
而
[
0
1
1
1
]
n
\begin{bmatrix} 0& 1\\ 1&1 \end{bmatrix}^n
[0111]n 的值可以用快速幂算出来,这样就可以
O
(
l
o
g
n
)
O(logn)
O(logn) 实现计算斐波那契数列的第
n
n
n 项了。
假设
g
(
i
)
g(i)
g(i) 代表求
a
a
a 的幂次的公式
g
(
i
+
2
)
=
g
(
i
)
+
g
(
i
+
1
)
+
1
g(i+2)=g(i)+g(i+1)+1
g(i+2)=g(i)+g(i+1)+1 ,矩阵的递推式如下:
[
0
1
0
1
1
1
0
0
1
]
∗
[
f
(
i
)
f
(
i
+
1
)
1
]
=
[
f
(
i
+
1
)
f
(
i
)
+
f
(
i
+
1
)
+
1
1
]
=
[
f
(
i
+
1
)
f
(
i
+
2
)
1
]
\begin{bmatrix} 0 & 1 & 0\\ 1 & 1 &1\\0 & 0 & 1\end{bmatrix} * \begin{bmatrix} f(i)\\ f(i+1) \\1 \end{bmatrix} =\begin{bmatrix} f(i+1)\\ f(i)+f(i+1)+1\\ 1 \end{bmatrix} =\begin{bmatrix} f(i+1)\\ f(i+2)\\ 1 \end{bmatrix}
⎣⎡010110011⎦⎤∗⎣⎡f(i)f(i+1)1⎦⎤=⎣⎡f(i+1)f(i)+f(i+1)+11⎦⎤=⎣⎡f(i+1)f(i+2)1⎦⎤
Code:
#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;
typedef long long ll;
struct Mat {
ll a[3][3];
};
Mat Mul(Mat a, Mat b, ll mod) { //矩阵乘法
Mat res;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
res.a[i][j] = 0;
for (int k = 0; k < 3; k++) {
res.a[i][j] += a.a[i][k] * b.a[k][j] % mod;
res.a[i][j] %= mod;
}
}
}
return res;
}
Mat power(Mat a, ll b, ll mod) { //矩阵快速幂a^b%mod
Mat res;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
res.a[i][j] = 0;
}
}
res.a[0][0] = res.a[1][1] = res.a[2][2] = 1;
while (b) {
if (b & 1)
res = Mul(res, a, mod);
b >>= 1;
a = Mul(a, a, mod);
}
return res;
}
ll feb(ll n, ll mod) { //求x、y的幂次
Mat base;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
base.a[i][j] = 0;
}
}
base.a[0][1] = base.a[1][1] = base.a[1][0] = base.a[1][2] = 1;
Mat res = power(base, n - 1, mod);
return (res.a[0][0] + res.a[0][1]) % mod;
}
ll feb2(ll n, ll mod) { //求a的幂次
Mat base;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
base.a[i][j] = 0;
}
}
base.a[0][1] = base.a[1][1] = base.a[1][0] = base.a[1][2] = base.a[2][2] = 1;
Mat res = power(base, n - 1, mod);
return (res.a[0][0] + 2 * res.a[0][1] + res.a[0][2]) % mod;
}
ll power(ll a, ll b, ll mod) { //快速幂a^b%mod
ll res = 1;
while (b) {
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int main() {
int m = 1e9 + 7;
ll n, x, y, a, b;
cin >> n >> x >> y >> a >> b;
if (n == 1) {
cout << x % m << endl;
return 0;
}
if (n == 2) {
cout << y % m << endl;
return 0;
}
if (x % m == 0 || y % m == 0 || a % m == 0) {
cout << 0 << endl;
return 0;
}
x %= m;
y %= m;
//这里要注意a对m取模
a = power(a % m, b, m);
//模m-1是因为费马小定理
cout << power(x, feb(n - 2, m - 1), m) * power(y, feb(n - 1, m - 1), m) % m * power(a % m, feb2(n - 2, m - 1) % (m - 1), m) % m << endl;
return 0;
}
dalao的题解
附 牛客的题解含标程
作者 神崎兰子
https://ac.nowcoder.com/discuss/364600?type=101&order=0&pos=2&page=3