【思路】赛内 问:以下哪个括号串是回文的: A:
(
(
)
(
)
)
(()())
(()()) B:
(
(
)
(
(
)
(()(()
(()(() C:
(
(
)
)
(
(
(())((
(())(( 答案是
c
\tiny{c}
c,
A
A
A 是镜像串
S
y
m
m
e
t
r
y
S
t
r
i
n
g
Symmetry\ String
SymmetryString。 那就送分题了。第一个位置为
)
)
) 或者
(
(
( 都会导致结果不合法,只有
n
=
0
n=0
n=0时才为
1
1
1(空串) 顺带一提,如果题目问的是镜像串的数量,那么首先判断
n
n
n 必须为偶数,然后
n
=
n
/
2
n=n/2
n=n/2,最终答案为
C
n
⌊
n
/
2
⌋
C_n^{\lfloor n/2\rfloor}
Cn⌊n/2⌋
B
\mathfrak{B}
B :系数 | 数学
【题意】 问你
(
x
2
+
x
+
1
)
n
(x^2+x+1)^n
(x2+x+1)n 的第
k
k
k 项系数取模
3
3
3 是多少。
【范围】 样例组数
T
≤
1
0
4
T\le 10^4
T≤104
0
≤
n
≤
1
0
15
0\le n\le 10^{15}
0≤n≤1015
0
≤
k
≤
2
×
n
0\le k\le 2\times n
0≤k≤2×n
【思路】题解 + 赛后 如果直接去做的话,是一个
O
(
n
k
)
O(nk)
O(nk) 的
d
p
dp
dp,式子如下:
d
p
[
i
]
[
j
]
=
{
1
j
=
0
或
者
j
=
2
i
d
p
[
i
−
1
]
[
j
−
1
]
+
d
p
[
i
−
1
]
[
j
]
j
=
1
d
p
[
i
−
1
]
[
j
−
2
]
+
d
p
[
i
−
1
]
[
j
−
1
]
j
=
2
i
−
1
d
p
[
i
−
1
]
[
j
−
2
]
+
d
p
[
i
−
1
]
[
j
−
1
]
+
d
p
[
i
−
1
]
[
j
]
O
t
h
e
r
s
dp[i][j]= \begin{cases} 1& j=0 或者 j=2i\\ dp[i-1][j-1]+dp[i-1][j]&j=1\\ dp[i-1][j-2]+dp[i-1][j-1]&j=2i-1\\ dp[i-1][j-2]+dp[i-1][j-1]+dp[i-1][j]&Others \end{cases}
dp[i][j]=⎩⎪⎪⎪⎨⎪⎪⎪⎧1dp[i−1][j−1]+dp[i−1][j]dp[i−1][j−2]+dp[i−1][j−1]dp[i−1][j−2]+dp[i−1][j−1]+dp[i−1][j]j=0或者j=2ij=1j=2i−1Others 应该没写错吧 这是一个不规则三角阵,貌似没法直接去做…有题解看出来是一个分形,进行具体分析的
%
%
%
\%\%\%
%%% 我们注意到一个细节:
输
出
取
模
3
的
系
数
输出取模3的系数
输出取模3的系数,原式子为
(
x
2
+
x
+
1
)
n
(x^2+x+1)^n
(x2+x+1)n 考虑同余,我们知道
(
x
2
+
x
+
1
)
n
≡
(
x
2
−
2
x
+
1
)
n
≡
(
x
−
1
)
2
n
(
m
o
d
3
)
(x^2+x+1)^n\equiv (x^2-2x+1)^n\equiv (x-1)^{2n}\pmod 3
(x2+x+1)n≡(x2−2x+1)n≡(x−1)2n(mod3) 然后直接二项式,答案就得到了:
(
−
1
)
2
n
−
k
C
2
n
k
(-1)^{2n-k}C_{2n}^k
(−1)2n−kC2nk 因为模数为
3
3
3 是一个质数,直接用
L
u
c
a
s
Lucas
Lucas 定理做就行了。
【代码】 时间复杂度:
T
log
n
T\log n
Tlogn
32
/
1000
M
s
32/1000Ms
32/1000Ms
【思路】 对于任意
x
x
x ,
x
n
x^n
xn的末三位都有循环节的。
x
=
5
x=5
x=5 的循环节如下:
005
,
025
,
125
,
625
,
125
,
625
⋯
005,025,125,625,125,625\cdots
005,025,125,625,125,625⋯
D
\mathfrak{D}
D : 划数 | QD数学
【题意】 有
n
n
n 个数,每次划去两个数
x
,
y
x,y
x,y,增加一个数
(
x
+
y
)
%
11
(x+y)\%11
(x+y)%11 划到只剩两个数时,一个数为
c
n
t
cnt
cnt,问你另个一数为几。
【范围】
c
n
t
≥
11
cnt\ge11
cnt≥11
2
≤
n
≤
15000
2\le n\le 15000
2≤n≤15000
1
≤
n
u
m
[
i
]
≤
1
0
6
1\le num[i]\le 10^6
1≤num[i]≤106
【思路】 剩下的那个
c
n
t
cnt
cnt 肯定是没划过的。
n
=
2
n=2
n=2,剩下的就是另一个数字。
n
>
2
n>2
n>2,剩下的就是其他所有划过的数字。等于其他数字的总和取模
11
11
11。
E
\mathfrak{E}
E :网格 |
d
p
dp
dp
【题意】
n
×
m
n\times m
n×m 的网格,每个位置有一个权值
a
i
,
j
a_{i,j}
ai,j,每个位置要从上下左右方向选择互相垂直的两个。 定义
w
(
x
)
=
x
+
p
o
p
c
n
t
(
x
)
w(x)=x+popcnt(x)
w(x)=x+popcnt(x),其中
p
o
p
c
n
t
(
x
)
popcnt(x)
popcnt(x) 表示
x
x
x 的二进制表示中
1
1
1 的个数。 如果两个相邻的位置
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1) 和
(
x
2
,
y
2
)
(x_2,y_2)
(x2,y2) 互相位与对方选择的方向之中,那么答案增加
w
(
a
x
1
,
y
1
⊕
a
x
2
,
y
2
)
w(a_{x_1,y_1}\oplus a_{x_2,y_2})
w(ax1,y1⊕ax2,y2),其中
⊕
\oplus
⊕ 表示二进制按位异或。 问你答案最大值为多少。
【范围】
1
≤
n
,
m
≤
1000
1\le n,m\le 1000
1≤n,m≤1000
0
≤
a
i
,
j
<
1024
0\le a_{i,j}<1024
0≤ai,j<1024
【思路】题解 赛内的时候感觉:方向好复杂!
w
(
x
)
w(x)
w(x) 好复杂!贡献还要异或!一堆给人不可做的感觉… 但是我们把要求
⌈
\lceil
⌈抽丝剥茧
⌋
\rfloor
⌋之后,就没有那么复杂了。 方向选择的要求是上下左右方向选择互相垂直的两个,我们改成:向左和向右选择一个方向,向上和向下选择一个方向。 先考虑左右方向。那么对答案的贡献只能是一行里面某两个相邻位置对答案有贡献。 这不就是最简单的
d
p
dp
dp 吗?设
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0] 表示当前行第
i
i
i 列选择方向向左,
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1] 表示选择方向向右。
d
p
[
i
]
[
1
]
=
max
{
d
p
[
i
−
1
]
[
0
]
,
d
p
[
i
−
1
]
[
1
]
}
d
p
[
i
]
[
0
]
=
max
{
d
p
[
i
−
1
]
[
0
]
,
d
p
[
i
−
1
]
[
1
]
+
w
(
贡
献
)
}
\begin{aligned} dp[i][1]&=\max\{dp[i-1][0],dp[i-1][1]\}\\ dp[i][0]&=\max\{dp[i-1][0],dp[i-1][1]+w(贡献)\} \end{aligned}
dp[i][1]dp[i][0]=max{dp[i−1][0],dp[i−1][1]}=max{dp[i−1][0],dp[i−1][1]+w(贡献)} 这个贡献,我们只要预处理好每个数字的
p
o
p
c
n
t
[
i
]
popcnt[i]
popcnt[i] 即可。 对于上下方向,和该转移式子差别不大。
【代码】 时间复杂度:
O
(
n
m
)
O(nm)
O(nm)
19
/
3000
M
s
19/3000Ms
19/3000Ms
constint MAX =1e3+50;int pop[MAX];int aa[MAX][MAX];
ll dp[MAX][2];intlowbit(int x){return(x &-x);}voidinit(int n){
pop[0]=0;for(int i =1;i <= n;++i){
pop[i]= pop[i-lowbit(i)]+1;}}intmain(){init(1024);int n,m;n =read();m =read();for(int i =1;i <= n;++i)for(int j =1;j <= m;++j)
aa[i][j]=read();
ll res =0;for(int i =1;i <= n;++i){
dp[1][0]= dp[1][1]=0;for(int j =2;j <= m;++j){
ll tmp = aa[i][j]^ aa[i][j-1];
ll duo = tmp + pop[tmp];
dp[j][1]=max(dp[j-1][0],dp[j-1][1]);
dp[j][0]=max(dp[j-1][0],dp[j-1][1]+ duo);}
res += dp[m][0];}for(int i =1;i <= m;++i){
dp[1][0]= dp[1][1]=0;for(int j =2;j <= n;++j){
ll tmp = aa[j][i]^ aa[j-1][i];
ll duo = tmp + pop[tmp];
dp[j][1]=max(dp[j-1][0],dp[j-1][1]);
dp[j][0]=max(dp[j-1][0],dp[j-1][1]+ duo);}
res += dp[n][0];}printf("%lld",res);return0;}
F
\mathfrak{F}
F :组合数问题 | 数学 + 打表
【题意】
4
∣
n
4|n
4∣n,求计算
C
n
0
+
C
n
4
+
C
n
8
+
⋯
+
C
n
n
C_n^0+C_n^4+C_n^8+\cdots+C_n^n
Cn0+Cn4+Cn8+⋯+Cnn
【范围】
1
≤
n
≤
1
0
18
1\le n\le 10^{18}
1≤n≤1018
【思路】赛内 前几项打表,得出式子:
t
=
n
/
4
t=n/4
t=n/4
O
E
I
S
OEIS
OEIS 之后得出答案:
a
n
s
=
(
−
4
)
t
×
1
2
+
1
6
n
×
1
4
ans=(-4)^t\times \frac{1}{2}+16^n\times \frac{1}{4}
ans=(−4)t×21+16n×41 虽然有点偷懒
G
\mathfrak{G}
G : 机器人 | 状压
d
p
dp
dp + 高精度
【题意】
n
n
n 个机器,第
i
i
i 个入读一个数字
x
x
x 就会输出
a
i
x
+
b
i
a_ix+b_i
aix+bi 你一开始有一个数字
x
x
x,每个机器只用一次,最终最大值为多少。
【范围】
1
≤
a
i
,
b
i
,
x
,
n
≤
20
1\le a_i,b_i,x,n\le 20
1≤ai,bi,x,n≤20
【思路】赛内
2
0
20
20^{20}
2020 爆
l
l
ll
ll 了,我最后用了
J
a
v
a
Java
Java 大数写,虽然也可以
_
_
i
n
t
128
\_\_int128
__int128 一个简单的状压
d
p
dp
dp,假设现在的机器人集合为
i
i
i,
d
p
[
i
]
dp[i]
dp[i] 表示选择完这个集合的机器人,输出的最大值
d
p
[
i
]
=
max
{
d
p
[
i
−
j
]
×
a
j
+
b
j
}
i
&
j
=
j
,
j
=
l
o
w
b
i
t
(
j
)
dp[i]=\max\{dp[i-j]\times a_j+b_j\}\qquad i\&j=j,j=lowbit(j)
dp[i]=max{dp[i−j]×aj+bj}i&j=j,j=lowbit(j) 很好懂的吧…
【代码】 时间复杂度:
O
(
2
n
×
n
)
O(2^{n}\times n)
O(2n×n)
2645
/
6000
M
s
2645/6000Ms
2645/6000Ms
import java.math.BigInteger;import java.util.Scanner;publicclassMain{static BigInteger[] A =newBigInteger[30];static BigInteger[] B =newBigInteger[30];static BigInteger[] dp =newBigInteger[1100000];staticint[] pw =newint[30];publicstaticvoidmain(String[] args){
Scanner read =newScanner(System.in);int n = read.nextInt();
BigInteger x = read.nextBigInteger();int ed =1;for(int i =0;i < n;++i){
A[i]= read.nextBigInteger();
B[i]= read.nextBigInteger();
pw[i]= ed;
ed = ed *2;}
ed--;
dp[0]= x;for(int i =1;i <= ed;++i){
dp[i]= BigInteger.ZERO;for(int j =0;j < n;++j){if((i & pw[j])>0){int re = i - pw[j];
BigInteger tmp = dp[re].multiply(A[j]);
tmp = tmp.add(B[j]);
dp[i]= dp[i].max(tmp);}}}
System.out.println(dp[ed]);}}
【补充】 这题也可以贪心排序,不过这个数据,出题人的本意是让我们做状压
d
p
dp
dp 的。
H
\mathfrak{H}
H :动态最小生成树
赛后看别人代码:暴力能过?暴力能过!暴力能过… 正解显然我不会。
I
\mathfrak{I}
I :贪吃蛇 | QD
B
F
S
BFS
BFS
【题意】 走迷宫,蛇每走一格,就长长一格。
【范围】
1
≤
n
,
m
≤
100
1\le n,m\le 100
1≤n,m≤100
【思路】赛内 走迷宫,就是无法走回头路,还是
B
F
S
BFS
BFS 就能搞定。
【补充】 这题无法到达输出
−
1
-1
−1
J
\mathfrak{J}
J : 天空之城 |
K
r
u
s
k
a
l
Kruskal
Kruskal
M
S
T
MST
MST
【题意】 有
n
n
n 个城市,
q
q
q 条边。每个边的权值表示走该路的花费时间。 给定一个起点
s
s
s。你希望走过所有城市,且花费时间最短。 你走过的边再走就会不花时间。
【范围】
1
≤
n
≤
5000
1\le n\le 5000
1≤n≤5000
1
≤
q
≤
200000
1\le q\le 200000
1≤q≤200000
1
≤
v
a
l
≤
1
0
9
1\le val\le 10^9
1≤val≤109
【思路】赛内 城市名字字符串转成编号。 走过的边再走不会花时间?那就是一个
M
S
T
MST
MST 的裸题。 写个 并查集然后用
K
r
u
s
k
a
l
Kruskal
Kruskal 即可。
【代码】 时间复杂度:
O
(
q
log
q
)
O(q\log q)
O(qlogq)
425
/
5000
M
s
425/5000Ms
425/5000Ms