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 nm 的矩形格点阵,可以找到多少个不同的“好三角形”?
由于答案可能过大,请对 1000000007 1000000007 1000000007 取模。
输入描述:
两个正整数和( 2 ≤ n , m ≤ 1 0 9 2 \le n,m \le 10^9 2n,m109
输出描述:
面积为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=(m2)m(n1)2+(n2)n(m1)2+(n1)(m2)(n2)2+(m1)(n2)(m2)2 ,每一步取模。注意不能重复。
A题

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)\% (100x)% 概率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 0x1001nab1000
输出描述:
一首歌得分的期望,保留两位小数。
输入
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 2n1000001ain
输出描述:
输出一个正整数,代表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} 3n1012
输出描述:
一个正整数,为 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 n1 条边。每个顶点被染成了白色或者黑色。
maki想知道,取两个不同的点,它们的简单路径上有且仅有一个黑色点的取法有多少?
注:
①树上两点简单路径指连接两点的最短路。
< p , q > <p,q> <p,q> < q , p > <q,p> <q,p> 的取法视为同一种。
输入描述:
第一行一个正整数 n n n 。代表顶点数量。( 1 ≤ n ≤ 100000 1 \le n \le 100000 1n100000
第二行是一个仅由字符 ‘B’ 和 ‘W’ 组成的字符串。第 i i i 个字符是 B 代表第 i i i 个点是黑色, W 代表第 i i i 个点是白色。
接下来的 n − 1 n-1 n1 行,每行两个正整数 x x x y y y ,代表 x x x 点和 y y y 点有一条边相连 ( 1 ≤ x , y ≤ n 1 \le x,y \le n 1x,yn)
输出描述:
一个正整数,表示只经过一个黑色点的路径数量。
样例输入
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 1kn200000
输入仅有一行,为一个长度为 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 1kn200000
输入仅有一行,为一个长度为 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 区间的长度,取最大值即可。
用两个数组ab分别记录 0 0 0 1 1 1 的位置,以 k k k 长度为间隔分别遍历数组ab。以遍历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[i1]1),ji+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 i1 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 1n3000001a,b,c109
第二行是一个长度为 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)=xf(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(i1)f(i2)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)=2f(2)=3f(3)=f(1)f(2)2=12f(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…… 00b2b4b7b12b 从第三项开始每一项为前两项之和加 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+61(mod1e9+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 xya 幂次的第 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} 010110011f(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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值