视频讲解:BV18o4y1y74h
A. Omkar and Bad Story
题目大意
如果数组
b
b
b 中任意两个元素之差的绝对值
∣
b
i
−
b
j
∣
|b_i-b_j|
∣bi−bj∣ 都在数组
b
b
b 中至少出现一次,则称数组
b
b
b 是好数组。
给定一个包含
n
(
2
≤
n
≤
100
)
n(2 \leq n \leq 100)
n(2≤n≤100) 的数组
a
(
−
100
≤
a
i
≤
100
)
a(-100 \leq a_i \leq 100)
a(−100≤ai≤100) ,问是否能向
a
a
a 中添加整数,使其变成一个不超过
300
300
300 个元素的好数组。
题解
好数组中没有负数。否则最大值减去负数,会得到一个更大的值。基于此,可以判断
a
a
a 数组能否变成一个好数组。
由于
a
i
≤
100
a_i \leq 100
ai≤100 ,因此包含元素
0
,
1
,
2
,
3
,
.
.
.
,
m
a
x
{
a
i
}
0,1,2,3,...,max\{a_i\}
0,1,2,3,...,max{ai} 的数组,必定可以由
a
a
a 添加新元素构成,且是一个好数组。
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int gcd(int a,int b)
{
return a%b?gcd(b,a%b):b;
}
int main()
{
int T,n,i,a[110];
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
if(a[1]<0)
{
printf("NO\n");
continue;
}
printf("YES\n%d\n",a[n]+1);
for(i=0;i<=a[n];i++)
printf("%d ",i);
puts("");
}
}
B. Prinzessin der Verurteilung
题目大意
给定一个长度为
n
(
1
≤
n
≤
1000
)
n(1 \leq n \leq 1000)
n(1≤n≤1000) 的由小写字母构成的字符串
s
s
s ,求其
M
E
X
MEX
MEX 。
一个字符串的
M
E
X
MEX
MEX 值,等于不在其连续子串中出现的最短且字典序最小的由小写字母构成的字符串。
题解
长度为
1
1
1 的由小写字母组成的字符串有
26
26
26 种;
长度为
1
1
1 的由小写字母组成的字符串有
2
6
2
=
676
26^2=676
262=676 种;
长度为
1
1
1 的由小写字母组成的字符串有
2
6
3
=
17576
26^3=17576
263=17576 种;
由于字符串长度不超过
1000
1000
1000 ,因此其
M
E
X
MEX
MEX 必定不超过
3
3
3 个字符,不妨全部枚举后线性查找。
时间复杂度
O
(
G
3
N
)
O(G^3N)
O(G3N) ,
G
=
26
,
N
≤
1000
G=26,N \leq 1000
G=26,N≤1000 ,最大不超过
2
⋅
1
0
7
2 \cdot 10^7
2⋅107 。
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
void add(string &s)
{
int inc=1;
for(int i=s.length()-1;i>=0&&inc;i--)
{
if(s[i]+inc>'z')
s[i]='a';
else
{
s[i]++;
inc=0;
}
}
if(inc)
s='a'+s;
}
int main()
{
int T,n;
string str,s;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
cin>>str;
for(s="a";str.find(s)!=string::npos;add(s));
cout<<s<<endl;
}
}
C. Diluc and Kaeya
题目大意
给定一个由 ‘D’ 和 ‘K’ 两种字符组成的字符串,求最多可以将其分为多少个连续子串,使其每个子串内的 ‘D’ 与 ‘K’ 数量比相同。
题解
易得每个子串内的数量比,等于整个字符串的数量比。
设
D
s
u
m
i
Dsum_i
Dsumi 和
K
s
u
m
i
Ksum_i
Ksumi 分别表示前
i
i
i 个字符中 ‘D’ 的数量和 ‘K’ 的数量。
设
d
p
i
dp_i
dpi 表示前
i
i
i 个字符最多可以分为多少子串,则有
d
p
i
=
m
a
x
{
d
p
j
}
+
1
dp_i=max \{dp_j\}+1
dpi=max{dpj}+1
( j ≤ i & & D s u m i : K s u m i = D s u m j : K s u m j ) (j \leq i \; \&\& \; Dsum_i:Ksum_i=Dsum_j:Ksum_j) (j≤i&&Dsumi:Ksumi=Dsumj:Ksumj)
实现时,由于同一比例下,越靠后的 d p j dp_j dpj 越大,因此可以用 map 维护某一比例的上一个 d p j dp_j dpj 值。
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN=500500;
int dp[MAXN],dnum[MAXN],knum[MAXN];
map<pair<int,int>,int> mp;
int gcd(int x,int y)
{
if(y==0)
return x;
return x%y?gcd(y,x%y):y;
}
int main()
{
int T,n,i,g;
char c;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
dnum[0]=knum[0]=0;
for(i=1;i<=n;i++)
{
scanf(" %c",&c);
dnum[i]=dnum[i-1]+(c=='D'?1:0);
knum[i]=knum[i-1]+(c=='K'?1:0);
}
mp.clear();
for(i=1;i<=n;i++)
{
g=gcd(dnum[i],knum[i]);
dp[i]=mp[make_pair(dnum[i]/g,knum[i]/g)]+1;
mp[make_pair(dnum[i]/g,knum[i]/g)]=dp[i];
}
for(i=1;i<=n;i++)
printf("%d ",dp[i]);
puts("");
}
}
D. Omkar and Medians
题目大意
给定一个包含 n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1 \leq n \leq 2 \cdot 10^5) n(1≤n≤2⋅105) 个整数的数组 b ( − 1 0 9 ≤ b i ≤ 1 0 9 ) b(-10^9 \leq b_i \leq 10^9) b(−109≤bi≤109) ,问是否存在一个包含 2 n + 1 2n+1 2n+1 个整数的数组 a a a ,使得 ∀ i ∈ [ 1 , n ] \forall i \in [1,n] ∀i∈[1,n] , a 1 , a 2 , . . . , a 2 i − 1 a_1,a_2,...,a_{2i-1} a1,a2,...,a2i−1 的中位数等于 b i b_i bi 。
题解
向数组 a a a 中添加 2 2 2 个新元素时,设排序后原中位数为 a x a_x ax ,新中位数为 a y a_y ay ,则有以下几种情况:
- 2 2 2 个新元素都比 a x a_x ax 大,则 y = i + 1 y=i+1 y=i+1 ;
- 2 2 2 个新元素都比 a x a_x ax 小,则 y = i − 1 y=i-1 y=i−1 ;
- 1 1 1 个元素比 a x a_x ax 小,另 1 1 1 个元素比 a x a_x ax 大,则 y = x y=x y=x ;
- 1 1 1 个元素和 a x a_x ax 相等,另 1 1 1 个元素比 a x a_x ax 大,则 y = x y=x y=x 或 y = x + 1 y=x+1 y=x+1 ,均有 a x = a y a_x=a_y ax=ay ;
- 1 1 1 个元素和 a x a_x ax 相等,另 1 1 1 个元素比 a x a_x ax 小,则 y = x y=x y=x 或 y = x − 1 y=x-1 y=x−1 ,均有 a x = a y a_x=a_y ax=ay ;
因此当 b i b_i bi 为 a 1 , a 2 , . . . , a 2 i − 1 a_1,a_2,...,a_{2i-1} a1,a2,...,a2i−1 的中位数, b i + 1 b_{i+1} bi+1 为 a 1 , a 2 , . . . , a 2 i + 1 a_1,a_2,...,a_{2i+1} a1,a2,...,a2i+1 的中位数时,必定只有以下三种情况:
- b i + 1 = b i b_{i+1}=b_i bi+1=bi ;
- b i + 1 b_{i+1} bi+1 是 b 1 , b 2 , . . . , b i + 1 b_1,b_2,...,b_{i+1} b1,b2,...,bi+1 中比 b i b_i bi 大的最小的数;
- b i + 1 b_{i+1} bi+1 是 b 1 , b 2 , . . . , b i + 1 b_1,b_2,...,b_{i+1} b1,b2,...,bi+1 中比 b i b_i bi 小的最大的数;
如果不满足上述三个条件之一的话,则不存在数组
a
a
a 。如果对于每对
b
i
b_i
bi 和
b
i
+
1
b_{i+1}
bi+1 都满足的话,则存在数组
a
a
a 。
实现时,可以用 multiset 加 iterator 判断。
参考代码
# include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN=200200;
int b[MAXN];
multiset<int> st;
multiset<int>::iterator it1,it2;
int main()
{
int T,i,n,flag;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&b[i]);
st.clear();
st.insert(b[1]);
flag=1;
for(i=2;i<=n;i++)
{
st.insert(b[i]);
if(b[i]==b[i-1])
continue;
it1=st.find(b[i]);
it2=st.find(b[i-1]);
if(it1!=st.begin())
--it1;
if(it2!=st.begin())
--it2;
if((*it1)!=b[i-1]&&(*it2)!=b[i])
flag=0;
}
if(flag)
printf("YES\n");
else
printf("NO\n");
}
}
E. Omkar and Forest
题目大意
有一个森林,可以用 n ( 1 ≤ n ≤ 2000 ) n(1 \leq n \leq 2000) n(1≤n≤2000) 行 m ( 1 ≤ m ≤ 2000 ) m(1 \leq m \leq 2000) m(1≤m≤2000) 列的非零整数矩阵表示,其满足以下条件:
- 对于任意两个相邻格子(四邻域),其差的绝对值不超过 1 1 1 ;
- 如果一个格子大于 0 0 0 ,则其必定大于其相邻的至少一个格子(四邻域);
现在只知道森林的一部分信息。每个格子用 ‘0’ 或 ‘#’ 表示。其中 ‘0’ 表示该格子必定为 0 0 0 ,’#’ 表示该格子可以是任意非零整数。
求符合条件的森林种数,答案对 1 0 9 + 7 10^9+7 109+7 取模。
题解
假设用 ‘#’ 表示的格子中,某些格子为 ‘0’ ,剩余格子为正整数,那么方案必定是唯一的。为正整数的格子,等于其到最近的 0 0 0 格子的曼哈顿距离( Δ x + Δ y \Delta x + \Delta y Δx+Δy )。证明如下:
- 若为正整数的格子数值
>
>
> 到最近的
0
0
0 格子的曼哈顿距离,则会存在一对相差超过
1
1
1 的相邻格子。
[ 0 1 2 4 ] \begin{bmatrix} 0 & 1 & 2 & 4\\ \end{bmatrix} [0124]
2 2 2 和 4 4 4 相差大于 1 1 1。 - 若为正整数的格子数值
<
<
< 到最近的
0
0
0 格子的曼哈顿距离,则会存在至少一个正整数格子,其周围没有更小的格子,例如:
[ 0 1 2 3 4 3 2 1 ] \begin{bmatrix} 0 & 1 & 2 & 3 & 4 & 3 & 2 & 1\\ \end{bmatrix} [01234321]
最后一个 1 1 1 周围没有更小的格子。
因此若确定了每个 ‘#’ 为 0 0 0 还是正整数,则确定了整张图的方案。
答案为
2
k
2^k
2k ,其中
k
k
k 为 ‘#’ 的个数。
若
k
=
n
⋅
m
k=n \cdot m
k=n⋅m,则还需再
−
1
-1
−1 ,以排除全为正整数的情况。
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const ll mod=1e9+7;
ll powmod(ll x,ll p)
{
ll ret=1;
while(p)
{
if(p&1)
ret=x*ret%mod;
x=x*x%mod;
p>>=1;
}
return ret;
}
int main()
{
int T,n,m,i,j,num,ans;
char c;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
num=0;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
scanf(" %c",&c);
if(c=='#')
num++;
}
ans=powmod(2,num);
if(num==n*m)
ans=(ans-1+mod)%mod;
printf("%d\n",ans);
}
}
F. Omkar and Akmar
题目大意
两个人在包含
n
(
2
≤
n
≤
2
⋅
1
0
6
)
n(2 \leq n \leq 2 \cdot 10^6)
n(2≤n≤2⋅106) 个格子的环形棋盘上玩游戏,格子编号从
1
1
1 到
n
n
n 。第
i
(
1
≤
i
≤
n
−
1
)
i(1 \leq i \leq n-1)
i(1≤i≤n−1) 号格子的下一个格子是第
i
+
1
i+1
i+1 号格子,第
n
n
n 号格子的下一个格子是第
1
1
1 号格子。
双方轮流在棋盘上放置 ‘A’ 或 ‘B’ ,每次只能放在空格子上,且相邻格子上的字母不能相同。最后无法操作的人失败。
已知双方都采用最优策略,求有多少种可能的行动序列,答案对
1
0
9
+
7
10^9+7
109+7 取模。
题解
参考了 xin_chen 的代码。
由于是博弈问题,先考虑下最后谁会获胜,以及获胜方式。
如果最后棋盘上有偶数个字母,则表示后手进行了最后一次操作,后手获胜;如果最后棋盘上有奇数个字母,则先手获胜。
并且最后棋盘上必定是 ABABAB… 或 BABABA… 的形式,相邻两个字母间有
0
0
0 个或
1
1
1 个空格。
如果最后棋盘上有奇数个字母,则必定存在两个相同的字母相邻。
- 若期间有 0 0 0 个空格,则不合法;
- 若期间有 1 1 1 个空格,则必定可以放置另一个字母在这空格上,即当前状态不是最终状态。
因此最终棋盘上必定只有偶数个字母,即后手获胜。
因为无论如何都是后手获胜,因此最优策略下的行动序列数量,就等于所有可能的下的序列数量。
设棋盘最后有
i
i
i 个空格,剩余
n
−
i
n-i
n−i 个有字母的格子,且
n
−
i
n-i
n−i 需要为偶数。
由于棋盘是环形的,不妨假设第一步填写在第
1
1
1 号格子上。
共
n
n
n 个格子中,有
i
i
i 个空格,不存在两个空格相邻,且第
1
1
1 个不是空格的方案数,等价于在
n
−
i
n-i
n−i 个格子中任意选择
i
i
i 个空格,并在每个空格前添加一个非空格的方案数
C
n
−
i
i
C_{n-i}^{i}
Cn−ii 。
接下来 n − i − 1 n-i-1 n−i−1 步分别填写剩余 n − i − 1 n-i-1 n−i−1 个非空格,方案数为 ( n − i − 1 ) ! (n-i-1)! (n−i−1)! 。
由于第一步实际上有 n n n 个格子可以选择,且可以选择 ‘A’ 或 ‘B’ 填写,因此事实上最终方案还要乘上 2 n 2n 2n 。
总方案数为
2
n
⋅
∑
i
=
0
⌊
n
2
⌋
(
(
n
−
i
)
%
2
=
=
0
)
⋅
C
n
−
i
i
⋅
(
n
−
i
−
1
)
!
2n \cdot \sum_{i = 0}^{\lfloor \frac{n}{2} \rfloor}{((n-i)\%2==0) \cdot C_{n-i}^i \cdot (n-i-1)!}
2n⋅i=0∑⌊2n⌋((n−i)%2==0)⋅Cn−ii⋅(n−i−1)!
参考代码
# include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN=1000100;
const ll mod=1e9+7;
ll jc[MAXN],invjc[MAXN];
ll powmod(ll x,ll p)
{
ll ret=1;
while(p)
{
if(p&1)
ret=ret*x%mod;
x=x*x%mod;
p>>=1;
}
return ret;
}
ll C(ll n,ll m)
{
if(m>n)
return 0;
return jc[n]*invjc[m]%mod*invjc[n-m]%mod;
}
int main()
{
int i,n;
ll ans;
jc[0]=1;
for(i=1;i<MAXN;i++)
jc[i]=jc[i-1]*i%mod;
invjc[MAXN-1]=powmod(jc[MAXN-1],mod-2);
for(i=MAXN-2;i>=0;i--)
invjc[i]=invjc[i+1]*(i+1)%mod;
scanf("%d",&n);
ans=0;
for(i=0;i<=n/2;i++)
{
if((n-i-1)&1)
ans=(ans+C(n-i,i)*jc[n-i-1])%mod;
}
ans=ans*2*n%mod;
printf("%lld\n",ans);
}
本文讲解了如何通过添加整数使数组成为好数组,包括数组中负数的存在与否、构造含有特定中位数的数组技巧,以及在森林和棋盘游戏中最优策略的应用。涉及算法包括数组操作、博弈论和组合数学。
1133





