DOJ-1910题单总结
纯想
CF353B
本题没有任何技术含量,若同一数出现 2 2 2 次及以上将其中 2 2 2 个分别放到不同堆中,其余统一分配。
CF359C
等图片
CF362C
冒泡排序交换次数等价于数组逆序对数量,暴力枚举交换的 2 2 2 个数,通过维护 2 2 2 个数组 O ( 1 ) O(1) O(1) 求出新数组逆序对数量
CF366C
自己想+题解点播
CF343C
自己的思考
首先看到最短时间,且时间越长肯定越可以实现,所以最短时间方面可以进行二分;
对于每个读取口,我想到了它可以一直往右或左、先往左再往右、先往右再往左的运动状态但并没有想到读取口该如何运动?
题解给的启发
首先时间是定的,对于所有读取口尽可能希望其能覆盖尽可能多的信息。
对于边界的读取口,用它读取同向边界的信息一定是很优秀的,所以对于边界的读取口,必须先将边界信息读取,再往回读取尽可能多的信息,不断出现新的边界,最终完成。
等图片
坑点
注意对于每个时间节点下的左边界,必须让其读取到那个时间点下最左边的信息(若它不能读到,其它节点必不能读到)。
反思
对于这道题,应先找到特殊的地方,读取头在边界的最特殊,应从它开始分析,我们的目的是让时间最少,时间最少代表每个读取头利用价值最大,也就代表要物尽其用从特殊考虑。
CF372B
一道典型的纯优化型dp,很有数学含量
O ( n 6 ⋅ q ) O(n^6 \cdot q) O(n6⋅q)(2定+暴力)
对于每个询问,直接枚举每个矩阵(左上,右下),暴力计算矩阵和。
O ( n 4 ⋅ q ) O(n^4 \cdot q) O(n4⋅q) (2定+二维前缀和)
提前用二维前缀和维护矩阵和,对于每个询问,直接枚举每个矩阵(左上,右下),通过二维前缀和调用矩阵和。
O ( n 2 ⋅ q ) O(n^2\cdot q) O(n2⋅q)(1定1动+2*二维前缀和)
定义
g
i
,
j
,
l
,
r
g_{i,j,l,r}
gi,j,l,r 表示以
(
i
,
j
)
(i,j)
(i,j) 为左上角,右下角在以
(
i
,
j
)
和
(
l
,
r
)
(i,j)和(l,r)
(i,j)和(l,r) 分别为左上角和右下角的矩形内的所有矩阵中矩阵和为
0
0
0 的矩阵数量。
对于每个询问,直接枚举每个矩阵(左上),通过
g
[
]
[
]
[
]
[
]
g[][][][]
g[][][][] 调用矩阵和为
0
0
0 的矩阵数量。
O ( q ) O(q) O(q)(2动+3*二维前缀和)
先维护
g
[
]
[
]
[
]
[
]
g[][][][]
g[][][][]
定义
f
i
,
j
,
l
,
r
f_{i,j,l,r}
fi,j,l,r 表示以
(
i
,
j
)
和
(
l
,
r
)
(i,j)和(l,r)
(i,j)和(l,r) 分别为左上角和右下角的矩阵的所有子矩阵中矩阵和为
0
0
0 的矩阵数量.
对于每个询问,通过
f
[
]
[
]
[
]
[
]
f[][][][]
f[][][][] 调用矩阵和为
0
0
0 的矩阵数量。
CF374C
坑点
记录DIMA 数量的 d i s [ ] [ ] dis[][] dis[][] 可以记录按照顺序经过的字母数量,其连续 DIMA 数量为 ⌊ m a x ( d i s [ ] [ ] ) / 4 ⌋ \lfloor max(dis[][])/4 \rfloor ⌊max(dis[][])/4⌋ ;
纯题解点播
CF356B
首先我们缩小范围,只需要研究
l
c
m
(
l
e
n
a
,
l
e
n
b
)
lcm(lena,lenb)
lcm(lena,lenb) 长度的价值,最后在乘上
l
e
n
a
⋅
n
l
c
m
(
l
e
n
a
,
l
e
n
b
)
\frac{lena\cdot n}{lcm(lena,lenb)}
lcm(lena,lenb)lena⋅n。
若罗列
l
c
m
(
l
e
n
a
,
l
e
n
b
)
lcm(lena,lenb)
lcm(lena,lenb) 长度字符串我们会发现
f
o
r
a
l
l
a
i
forall a_i
forallai 都恰好与
b
j
(
i
m
o
d
gcd
(
l
e
n
a
,
l
e
n
b
)
=
j
m
o
d
gcd
(
l
e
n
a
,
l
e
n
b
)
)
b_j(i \mod \gcd(lena,lenb)=j \mod \gcd(lena,lenb))
bj(imodgcd(lena,lenb)=jmodgcd(lena,lenb)) 对应一次
CF351B
主要是态度问题,期望的题还是要想15min的。
直观思路
我们将
2
2
2 个人的策略分开讨论:
Jeff: 肯定会减少
1
1
1 个逆序对
Fruik: 50% -> +1 50%->-1 ,综上期望上逆序对数不变
结合
2
2
2 个人分别操作一次,期望减少的逆序对数量为
1
1
1
因此,设原数组逆序对数量为
t
o
t
tot
tot
若
t
o
t
tot
tot 为偶数 答案为
2
t
o
t
2tot
2tot
否则 答案为
2
t
o
t
−
1
2tot-1
2tot−1
严谨思路
首先根据期望的线性性和可加性,开始进行dp转移。
定义
f
i
f_i
fi 表示有
i
i
i 个逆序对所要进行的操作数期望
状态转移方程:
f
i
=
1
2
(
f
i
−
2
+
2
)
+
1
2
(
f
i
+
2
)
f_i=\frac{1}{2}(f_{i-2}+2)+\frac{1}{2}(f_i+2)
fi=21(fi−2+2)+21(fi+2)
变换一下转移方程得到:
f
i
=
f
i
−
2
+
4
f_i=f_{i-2}+4
fi=fi−2+4
∵
f
0
=
0
,
f
1
=
1
\because f_0=0,f_1=1
∵f0=0,f1=1
∴
f
i
=
[
i
m
o
d
2
=
1
]
⋅
(
2
i
−
1
)
+
[
i
m
o
d
2
=
0
]
⋅
2
i
\therefore f_i=[i \mod 2=1]\cdot(2i-1)+[i \mod 2=0] \cdot 2i
∴fi=[imod2=1]⋅(2i−1)+[imod2=0]⋅2i
完整且严谨
期望可加性证明:
等图片
CF367B
map 可以直接比较是否相等
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+10;
int n,m,p,l,r,num;
int a[maxn],ans[maxn];
map<int,int>b,cnt;
inline void ipt(){
scanf("%lld%lld%lld",&n,&m,&p);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=m;i++){
int x;
scanf("%lld",&x);
b[x]++;
}
}
inline void solve(){
for(int i=1;i<=p;i++){
cnt.clear();
l=r=0;
for(int j=i;j<=n;j+=p){
++r;
cnt[a[j]]++;
if(r-l==m){
if(b==cnt)ans[++num]=j-(m-1)*p;
cnt[a[j-(m-1)*p]]--;
if(cnt[a[j-(m-1)*p]]==0)cnt.erase(a[j-(m-1)*p]);
++l;
}
}
}
}
inline void opt(){
sort(ans+1,ans+num+1);
printf("%lld\n",num);
for(int i=1;i<=num;i++)printf("%lld ",ans[i]);
printf("\n");
}
signed main(){
ipt();
solve();
opt();
return 0;
}