E - Integer Sequence Fair
题意:
P
=
998244353
P=998244353
P=998244353输入三个数,
M
,
N
,
K
M,N,K
M,N,K 就是需要求
M
k
N
m
o
d
M^{k^N} mod
MkNmod
P
P
P
思路: 由于数据范围很大 所以需要先处理
K
N
K^N
KN,再处理
M
k
N
M^{k^N}
MkN
根据费马小定理 如果
g
c
d
(
a
,
p
)
=
1
gcd(a,p)=1
gcd(a,p)=1那么
a
p
−
1
≡
1
a^{p-1}≡1
ap−1≡1(
m
o
d
p
mod p
modp)
在这个题中,由于
P
P
P是素数,所以除了
M
M
M是
P
P
P的倍数以外的情况, 一定会出现
g
c
d
(
P
,
M
)
=
1
gcd(P,M)=1
gcd(P,M)=1
所以分类讨论即可
1.
M
M
M是
P
P
P的倍数,那么
M
m
o
d
P
=
0
MmodP=0
MmodP=0所以输出0即可
令
q
q
q为
K
N
K^N
KN除以
P
−
1
P-1
P−1的商,
r
r
r为
K
N
K^N
KN除以
P
−
1
P-1
P−1的余数
2.
M
M
M不是
P
P
P的倍数
令
q
q
q为
K
N
K^N
KN除以
P
−
1
P-1
P−1的商,
r
r
r为
K
N
K^N
KN除以
P
−
1
P-1
P−1的余数根据上面的结论,
M
K
N
M^{K^N}
MKN
=
M
q
(
P
−
1
)
+
r
=
M
q
(
P
−
1
)
×
M
r
≡
1
q
×
M
r
≡
M
r
(
m
o
d
P
)
=M^{q(P-1)+r}=M^{q(P-1)}×M^r≡1^q×M^r≡M^r(modP)
=Mq(P−1)+r=Mq(P−1)×Mr≡1q×Mr≡Mr(modP)
所以就是先算
K
N
m
o
d
(
P
−
1
)
K^Nmod(P-1)
KNmod(P−1)为
t
e
m
p
temp
temp,再算
M
t
e
m
p
(
m
o
d
P
)
M^{temp}(modP)
Mtemp(modP)
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
const int MOD = 998244353;
int phi(ll x) {
ll res = x;
for (ll i = 2; i <= x / i; i++) {
if (x % i == 0) {
res = res / i * (i - 1);
while (x % i == 0) {
x /= i;
}
}
}
if (x > 1) res = res / x * (x - 1);
return res;
}
int qmi(int a, int k, int mod) {
int res = 1;
a %= mod;
while (k) {
if (k & 1) res = res * a % mod;
a = a * a % mod;
k >>= 1;
}
return res;
}
signed main() {
int n, k, m;
cin >> n >> k >> m;
ll p = phi(MOD);
if (m % MOD == 0) {
cout << 0 << endl;
return 0;
}
int temp = qmi(k, n, p);
int res = qmi(m, temp, MOD);
cout << res << endl;
}
在快速幂中之所以要特判
M
≡
0
(
m
o
d
p
)
M≡0(modp)
M≡0(modp)的情况,是因为如果出现指数变成
0
0
0,那么写的快速幂由于没有特判就会直接输出
1
1
1,但是本应该输出
0
0
0
但是也可以不特判,因为只要保证指数
>
0
>0
>0即可,因为通过前提推论可知,
M
P
−
1
≡
1
(
m
o
d
P
)
M^{P-1}≡1(modP)
MP−1≡1(modP),所以可以直接在第一次的快速幂中加上
P
−
1
P-1
P−1
不会影响等式,因为
M
K
N
×
M
P
−
1
≡
M
K
N
(
m
o
d
P
)
M^{K^N}×M^{P-1}≡M^{K^N}(modP)
MKN×MP−1≡MKN(modP)
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
const int MOD = 998244353;
int phi(ll x) {
ll res = x;
for (ll i = 2; i <= x / i; i++) {
if (x % i == 0) {
res = res / i * (i - 1);
while (x % i == 0) {
x /= i;
}
}
}
if (x > 1) res = res / x * (x - 1);
return res;
}
int qmi(int a, int k, int mod) {
int res = 1;
a %= mod;
while (k) {
if (k & 1) res = res * a % mod;
a = a * a % mod;
k >>= 1;
}
return res;
}
signed main() {
int n, k, m;
cin >> n >> k >> m;
ll p = phi(MOD);
int temp = qmi(k, n, p) + p;
int res = qmi(m, temp, MOD);
cout << res << endl;
}
F - Stamp Game.
题意:
给定一个
N
×
M
N×M
N×M的矩阵
A
i
,
j
A_{i,j}
Ai,j,高桥可以将
h
1
×
w
1
h_1×w_1
h1×w1大小内的矩阵涂黑,即将这个区域内的格子涂满黑色,青木可以将
h
2
×
w
2
h_2×w_2
h2×w2大小内的矩阵涂白,一开始矩阵内的方格都是白色的,最终高桥的分数就是矩阵内黑色方格的分数总和,高桥相让分数最大化,青木想让他的分数最小化,两人都是最聪明的,问双方涂完色后,高桥可能得到的最高分数是多少。
思路:
高桥的每个对应的矩阵的分数总和就是
s
1
(
i
T
,
j
T
)
=
∑
i
=
i
T
i
T
+
h
1
−
1
∑
j
=
j
T
j
T
+
w
2
−
1
A
i
,
j
s1(i_T,j_T)=\sum \limits_{i=i_T} ^{i_T+h_1-1}\sum \limits_{j=j_T} ^{j_T+w_2-1}A_{i,j}
s1(iT,jT)=i=iT∑iT+h1−1j=jT∑jT+w2−1Ai,j
青木覆盖的面积分数和为
s
2
(
i
T
,
j
T
)
=
∑
i
=
i
T
i
T
+
h
1
−
1
∑
j
=
j
T
j
T
+
w
2
−
1
A
i
,
j
s2(i_T,j_T)=\sum \limits_{i=i_T} ^{i_T+h_1-1}\sum \limits_{j=j_T} ^{j_T+w_2-1}A_{i,j}
s2(iT,jT)=i=iT∑iT+h1−1j=jT∑jT+w2−1Ai,j
最终答案就是
M
(
i
t
,
j
t
)
=
s
1
(
i
t
,
j
t
)
−
m
a
x
[
s
2
(
i
,
j
)
]
(
i
t
≤
i
≤
i
t
+
h
1
−
h
2
,
j
t
≤
j
≤
j
t
+
w
1
−
w
2
)
M(i_t,j_t)=s1(i_t,j_t)-max[s2(i,j)](i_t≤i≤i_t+h_1-h_2,j_t≤j≤j_t+w_1-w_2)
M(it,jt)=s1(it,jt)−max[s2(i,j)](it≤i≤it+h1−h2,jt≤j≤jt+w1−w2)
最终两重循环找
M
(
i
,
j
)
M(i,j)
M(i,j)中的最大值
需要通过滑动窗口或者线段树或者ST表来加速寻找
m
a
x
[
s
2
(
i
,
j
)
]
max[s2(i,j)]
max[s2(i,j)],从而算出
M
(
i
,
j
)
M(i,j)
M(i,j)
下面是滑动窗口的做法,先固定行然后对列进行滑动窗口操作,求出窗口内的最大值,窗口大小就是
m
1
−
m
2
+
1
m_1-m_2+1
m1−m2+1,定义
f
(
i
,
j
)
f(i,j)
f(i,j)为固定行后,算出的青木覆盖的最大分数,再固定列然后对行进行滑动窗口,窗口大小就是
h
1
−
h
2
+
1
h_1-h_2+1
h1−h2+1,定义
g
(
i
,
j
)
g(i,j)
g(i,j)就是在矩阵右下角是
(
i
,
j
)
(i,j)
(i,j)大小为
h
1
×
w
1
h_1×w_1
h1×w1的矩阵中,覆盖大小为
h
2
×
w
2
h_2×w_2
h2×w2矩阵的最大值,算出
g
(
i
,
j
)
g(i,j)
g(i,j),最后在循环跑最大值即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
using ll = long long;
ll a[N][N];
ll s[N][N];
ll s1[N][N];
ll s2[N][N];
ll f[N][N], g[N][N];
int n, m;
int n1, n2, m1, m2;
ll get(int x2, int y2, int x1, int y1) {
return s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
}
int main() {
cin >> n >> m >> n1 >> m1 >> n2 >> m2;
n2 = min(n1, n2);
m2 = min(m1, m2);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
}
//第一个人能删除的矩阵和
for (int i = n1; i <= n; i++) {
for (int j = m1; j <= m; j++) {
s1[i][j] = get(i, j, i - n1 + 1, j - m1 + 1);
}
}
//第二个人能覆盖的矩阵和
for (int i = n2; i <= n; i++) {
for (int j = m2; j <= m; j++) {
s2[i][j] = get(i, j, i - n2 + 1, j - m2 + 1);
}
}
//固定行,滑动窗口为列
for (int i = 1; i <= n; i++) {
multiset<ll> q;
for (int j = 1; j <= m; j++) {
q.insert(s2[i][j]);
if (j > (m1 - m2 + 1)) {
auto it = q.find(s2[i][j - (m1 - m2 + 1)]);
q.erase(it);
}
f[i][j] = *q.rbegin();
}
}
//固定列,滑动窗口为行
for (int j = 1; j <= m; j++) {
multiset<ll> q;
for (int i = 1; i <= n; i++) {
q.insert(f[i][j]);
if (i > n1 - n2 + 1) {
auto it = q.find(f[i - (n1 - n2 + 1)][j]);
q.erase(it);
}
g[i][j] = *q.rbegin();
}
}
ll res = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
res = max(res, s1[i][j] - g[i][j]);
}
}
cout << res << endl;
}
To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激