CF题解合集
CF 比赛题解合集
↓2023.09.04↓2023.09.04
CF1952
,CF1954
1952
A. Ntarsis' Set
有一个集合,初始状态里面有数字 11、22、33、44、55、......、101000101000。
现在给你一个长度为 nn 数组 a(1≤ai≤109)a(1≤ai≤109),要进行 kk 次操作,每次操作将当前集合中第 a1a1 小、第 a2a2 小、......、第 anan 小的数同时移除。
请问 kk 次操作之后,最小的数是多少。
顺难则逆。
考虑如果已知删除后在集合中的位置,可以很简单的找到删除前的位置。
所以已知初始的位置,也可以很简单的找到删除后的位置,O(n+k)O(n+k) 即可。
void work() {
int n, k; cin >> n >> k;
for (int i = 1; i <= n; ++i) cin >> a[i];
lint i = 1, cur = 1, pre = 0;
while (k--) {
cur += pre;
while (i <= n && a[i] <= cur)
++cur, ++i, ++pre;
}
cout << cur << '\n';
}
B. Imbalanced Arrays
对于一个给定的长度为 nn 的数组 AA,定义一个长度为 nn 的数组 BB 是不平衡的当且仅当以下全部条件满足:
-
−n≤Bi≤n−n≤Bi≤n 且 Bi≠0Bi≠0。即每个数在 [−n,n][−n,n] 内且不为 00。
-
∀i,j∈[1,n],Bi+Bj≠0∀i,j∈[1,n],Bi+Bj≠0。即数组内不存在一对相反数。
-
∀i∈[1,n],∑nj=1[(Bi+Bj)>0]=Ai∀i∈[1,n],∑nj=1[(Bi+Bj)>0]=Ai。即对于任意的 ii,数组中与 BiBi 和大于 00 的数的个数恰好为 AiAi。注意:这里需要计算本身。也即 ii 与 jj 可以相等。
请构造长度为 nn 的不平衡序列。
两种方法,其一可以参见 hfjh
。
考虑递归的构造序列,如果当前存在 Ai=0Ai=0 或者 Ai=nAi=n,那么其对应所填的数必定为 −n−n 或者 nn。
由于相反数不可能同时出现,所以两个不能同时存在,否则无解。
递归的构造即可。
代码链接:https://codeforces.com/contest/1852/problem/B
C. Ina of the Mountain
-
给定一个长度为 nn 的序列 aa 和正整数 kk。
-
每次可以选择一个区间 [l,r][l,r],∀i∈[l,r],ai=ai−1∀i∈[l,r],ai=ai−1。
-
如果 ai=0ai=0,则将 aiai 变为 kk。
求让序列全部为 kk 的最小操作次数。
多组测试数据,1≤n≤2×1051≤n≤2×105。
先不考虑 modkmodk 的情况,则构造差分序列 dd,答案即为:
那么考虑每次 0→k0→k 的变换,相当于在原序列上加了 kk,然后求上式。
考虑变成差分序列上加减。也就是有地方 +k+k,有地方 −k−k,使得上式最小。
所以考虑正负平衡一下即可。
代码链接:https://codeforces.com/contest/1852/submission/221761642
能考场切自是极好。
D. Miriany and Matchstick
你有一个 2×n2×n 的矩阵,第一行给定(字符串 ss),你需要在第二行的位置填上 AA 或 BB。
你需要构造矩阵的第二行,使得该矩阵相邻的格子不同的个数恰好为 kk。
考虑很 naive
的 DP fi,0/1,kfi,0/1,k 表示填完前 ii 个数,恰好为 kk 是否可行。
打表可以发现,kk 可行的集合至多为两个区间。
证明:每一次转移可以是 +1+1 或者 +2+2,同时又是前面的两个区间并过来的,所以空缺只会是 0/10/1 个。
所以可以压成 fi,0/1fi,0/1 即可 /kel
所以可以用
bitset
对吧。
最后贪心的构造即可。
E. Rivalries
Ntarsis 有一个长为 nn 的数组 aa。
定义一个子数组 al⋯aral⋯ar 的权重为:
- 在整个数组 aa 中,只出现在该子数组 al⋯aral⋯ar中的最大元素 xx。
- 若不存在这样的 xx,权重为 00。
称数组 bb 与 aa 数组匹配,当且仅当满足:
- 22 者长度相等。
- 数组 aa 中的每个子数组的权重都与数组 bb 对应的子数组的权重相等。
- bb 中的数都为正数。
最大化 bb 的权值和。
1≤n,t≤1051≤n,t≤105
考虑几个 hints
-
对于每一个值,只有最左边和最右边的两个之外的区间可以做出贡献。
-
将最左和最右记成二元组,如果存在 ai>ajai>aj 并且 ii 完全被 jj 覆盖,那么 jj 将无法做出任何贡献。
-
为了不改变权重,我们只可能增加上面所说的 jj 区间。
-
并且至多会有一个区间会加上某一个数。
1954
A. Dual
有 tt(1≤t≤5001≤t≤500)组数据,对于每组数据给出一个长度为 nn(1≤n≤201≤n≤20)的序列 aa(−20≤ai≤20−20≤ai≤20)。
可以进行 kk(0≤k≤310≤k≤31)次操作,每次操作任选一组 i,ji,j(1≤i,j≤n1≤i,j≤n),把 ai←ai+ajai←ai+aj,最后使得整个序列单调不减。
对于每组数据,第一行输出 kk,之后 kk 行输出执行操作的 i,ji,j。
超级分讨。
考虑到全部变成正的,或者全部变成负的,然后前缀/后缀和即可,这需要 1919 次。
首先找到绝对值最大的那个数。
如果异号的个数 ≤12≤12,那么直接加上即可。
反之,则同好的个数 ≤7≤7,那么找到最大的正数,做 55 次自加操作,然后加上去即可。
代码链接:Submission #221821611 - Codeforces
B. Earn or Unlock
有一长度为 nn 的一副牌,每张牌上都有一个数字,设第 ii 张牌上的数字为 aiai。初始时,你手里只有第一张牌。对于每一张牌,你有两种选择:
-
如果剩余的牌数量 <ai<ai,则将牌摸完,否则继续往下摸 aiai 张牌。摸牌完成后,这张牌会被丢弃。
-
获得 aiai 的分数,并丢弃这张牌。
求你能获得的最大分数。
对于所有数据,保证 1≤n≤1051≤n≤105,0≤ai≤n0≤ai≤n。
考虑如果可以摸完 kk 张牌,由于摸一张牌的代价为 11,所以得分为:
所以判断哪些地方可以摸到即可。
考虑一个 naive
的 O(n2)O(n2) DP。
有:
于是可以考虑用 bitset
优化。
所以总的复杂度为 O(n2w)O(n2w)。
代码:https://codeforces.com/contest/1854/submission/221792387
C. Expected Destruction
给定大小为 nn 的正整数集合 SS,SS 中的每个数在 1∼m1∼m 之间。
每一秒进行如下操作:
- 从 SS 中等概率随机选择一个数 xx。
- 将 xx 从 SS 中删去。
- 若 x+1≤mx+1≤m 且 x+1∉Sx+1∉S,则将 x+1x+1 加入 SS。
求 SS 变成空集的期望时间,对 109+7109+7 取模。
1≤n≤m≤5001≤n≤m≤500,1≤S1<S2<⋯<Sn≤m1≤S1<S2<⋯<Sn≤m。
1843
Div. 3 果然简单,虽然但是,我还是有 1 道题没有想出来。
A.Sasha and Array Coloring
排序双指针向内即可。
https://codeforces.com/contest/1843/submission/210855587
B.Long Long
好啊,就是这道题没想出来。
Virtual Contest
上完成了一半。
考虑把符号相同(0 与任何数符号相同)的合并。
因为放在一起操作显然最优。
bool sgn_eq(int x, int y) {
return (x * y) >= 0;
}
然后你就得到了一个 ... + - + - ...
正负交替的序列。
那么,很明显,正数与负数的个数相差不超过一,也就是说花费 1 的代价将负数转化为正数的行为是不可取的(我就死在这上面,如此弱智的东西……),于是记录所有负数的个数即可。
有可能溢出,要精细实现
https://codeforces.com/contest/1843/submission/211085413
也可以直接合并负数序列(忽略 0 的情况)
https://codeforces.com/contest/1843/submission/211082437
C.Sum in Binary Tree
会手写堆的人都会写……
Submission #210856724 - Codeforces
D.Apple Tree
弱智题……记录一下每一个节点有多少个叶子节点。输出相乘的结果即可。
https://codeforces.com/contest/1843/submission/210857337
E.Tracking Segments
其实可以改一下题目,变为:
对于每一条线段,求出其变成
beautiful segment
的时间,如果不会变,则输出-1
,顺便强制在线一波。这样代码难度递增。
我首先想到的是一个在线做法。
只要在这个区间内修改了严格大于一半的点,那么一个区间就变得 美丽。
那么什么最早是什么时候?
定义序列 titi 表示第 ii 个点变成 11 的时间,如果没有修改则 ti=infti=inf。
对于区间 [l,r][l,r],设 k=⌊l−r+12⌋+1k=⌊l−r+12⌋+1,则对于序列 tt 的区间 [l,r][l,r] 第 kk 小即为所求。
如果为 infinf 则不会变得 美丽。
但是显然这太过于复杂。
考虑离线处理每一个线段,并且二分答案。
明显答案具有单调性,如果在 tt 时变得 美丽,则之后一直会很 美丽。
我说的不是我们
我们只加入前 midmid 个点,然后用一个前缀和一一判断有没有 美丽 的线段即可。
如果是 美丽 的,则满足在这个区间内有 ≥k≥k 个点变成了 11。
https://codeforces.com/contest/1843/submission/211083551
F.Omsk Metro
自己做的时候胡的性质:
对于一个路径 pp,定义其最大子段和为 mxpmxp,最小子段和为 mnpmnp。
那么可以拼凑出来的区间为 [mxp,mnp][mxp,mnp]。
感性证明一下:
这里不妨把一条路径拍扁成一条链,设为 [l,r][l,r]。
我们考虑加入一个点 rr 对于一条路径 [l,r][l,r] 的影响。
设 sufxsufx 为以 xx 为右端点,可以凑出来的权值的集合。
考虑递推,有 sufx={0,w(x)}∪(sufx−1+w(x))sufx={0,w(x)}∪(sufx−1+w(x))。
于是对于区间 [l,r][l,r] 有 S=⋃l≤x≤rsufxS=⋃l≤x≤rsufx。
其实不难发现,sufxsufx 的上下界每一次最多只会变化 11,也就是说,如果可以凑出 x(x≥0)x(x≥0),那么一定可以凑出 [0,x][0,x]。负数的情况同理。
所以我们考虑求出最大的,和最小的子段和即可 O(1)O(1) 判断……
实现上也就是树链剖分加上基本的求区间最大子段和的思路即可。
1834
Virtual Contest 做了 5 道题,非常不错。
A.Unit Array
秒切题,判断个数,然后判断一下奇偶即可。
提交:https://codeforces.com/contest/1834/submission/211190220
B.Maximum Strength
题目描述
每一种材料的力量由一个十进制整数表示。
对于一个武器,由两种材料构成。假如第一种材料的力量为 X=¯x1x2…xnX=¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯x1x2…xn,第二种材料的力量为 Y=¯y1y2…ynY=¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯y1y2…yn,那么这个武器的力量为 ∑ni=1|xi−yi|∑ni=1|xi−yi|,也就是 |x1−y1|+|x2−y2|+⋯+|xn−yn||x1−y1|+|x2−y2|+⋯+|xn−yn|。
如果两个数字长度不一样,则要将短的数字用前导零补成相同长度。
现在,你拥有无数个力量在 [L,R][L,R] 中的材料。你想要选出两个材料做出一个力量最大的武器。请你求出你设计出的武器力量的最大值。
题解
找到第一个不同的地方,其他地方全部填 |9−0||9−0| 即可。
大数填 0,小数填 9,这样一定满足在区间内。
提交:Submission #211191108 - Codeforces
C.Game with Reversing
题目描述
你有两个长度为 nn 的序列 S,TS,T。
你将不断重复以下步骤直到两个序列相等:
-
在 SS 或者 TT 中任意选择一个数字,然后修改为另一个数字。如果此时序列相等,则结束操作,否则进入操作 22。
-
任意反转一个序列。如果此时序列相等,则结束操作,否则重复操作 1。
注意:操作 11 和操作 2 都计入操作次数中。
你需要输出最小化的操作次数。
解题
正序和逆序分别讨论一下,取 minmin 即可。
设 icnticnt 表示正序不同的个数,rcntrcnt 表示逆序不同的个数。
那么 ans=min(2×icnt−icnt%2,2×rcnt−(rcnt−1)%2)ans=min(2×icnt−icnt%2,2×rcnt−(rcnt−1)%2)。
提交:https://codeforces.com/contest/1834/submission/211198802
D.Survey in Class
对于每一个学生的学习集合,答案是 2×max(|S1|−|S1∩S2|)2×max(|S1|−|S1∩S2|)。
O(nlogn)O(nlogn)
考虑使用树状数组维护线段,从左向右扫一遍。
O(n)O(n)
考虑两条线段的关系,三种:
-
左边
-
相交
-
右边
对于每一个 S1S1,我们需要最小化 |S1∩S2||S1∩S2|,所以对于三种情况,我们维护三个线段:
-
右端点最左
-
最短
-
左端点最右
分别判断即可。
提交:Submission #211192375 - Codeforces
E.MEX of LCM
我考场上的做法
考虑只有 O(n2)O(n2) 个取值,那么 mexmex 一定 ≤n2≤n2。所以我们限制了答案的上界,设为 UU。
实际上有更紧的上界,根据:CF1834E MEX of LCM 题解 - LCTTTTTTTTTTT - 洛谷博客,UU 可以 =4256233=4256233。
考虑对于一个固定的右端点,其左端点与其匹配的 lcmlcm 是单调的,并且不同的数量在 O(logU)O(logU) 。
考虑每一次变化 lcmlcm 至少 ×2×2。
所以考虑用一个 set
维护。
同时,再用一个 set
维护所有出现过的 lcm
(考虑是 O(nlogU)O(nlogU) 个,空间没问题)
所以总复杂度为 O(nlogVlogUloglogU)O(nlogVlogUloglogU)。
提交:Submission #211194246 - Codeforces
还有的思路
考虑每一个左端点,可以二分找 lcm 的分界,当值大于上界的时候退出。
用桶或者什么东西操作一下即可。
复杂度为 O(nlogUlogn)O(nlogUlogn)。
然而还有玄学优化,参考:出错了 - 洛谷,在 题解界面 看 @Xy_top 的即可。
↓2023.09.06↓2023.09.06
1830
B. The BOSS Can Count Pairs
多组数据。
每组数据给你一个 nn 和两个序列 a,ba,b。
求有多少个数对 (i,j)(i,j) 满足 1≤i<j≤n1≤i<j≤n 且 ai×aj=bi+bjai×aj=bi+bj
对于每一个 ii 看作用 ai×x−bi=yai×x−bi=y 这条线来切所有的点。
注意到当 aiai 很大的时候的简单的,我们只需要用一个桶记录一下 (x,y)(x,y) 的个数即可,然后可以 O(nai)O(nai) 求出。
此时设一个阈值 BB,当 ai>Bai>B 的时候认为是很大的,所以上面的复杂度可以看作 O(nB)O(nB)。
问题在于当 aiai 很小的时候,注意到其实可以 O(B)O(B) 预处理出 ∀k∈[1,B]∀k∈[1,B] k×ai−bik×ai−bi 的值。
这样就可以 O(1)O(1) 查询了。
两者稍微平衡一下,令 B=√2nB=√2n 于是可以 O(n√n)O(n√n) 解决本题。
代码链接: https://codeforces.com/contest/1830/submission/222008847
C. Hyperregular Bracket Strings
给定一个数 nn 和 kk 个区间 [li,ri]∈[1,n][li,ri]∈[1,n]。
我们定义,对于一个长度为 nn 的,仅由 (
和 )
组成的合法括号序列,如果它的每一个区间 [li,ri][li,ri] 内的子串都是合法括号序列,那么这个括号序列是好的。
求好的括号序列的数量,答案对 998244353998244353 取模。
相交和包含的情况很 naive
,重点在于如何处理。
类比 2023.09.052023.09.05 izumi
,采用一个随机化异或的做法。
每一个区间附上一个神秘的权值,差分前缀一次。
如果异或权值相同,意味着需要形成一个合法括号,这预处理卡特兰数即可。
rapper
的讲解见:hfjh
代码链接:https://codeforces.com/contest/1830/submission/222007540
D. Mex Tree
给定一棵 nn 个结点的树,点有点权,点权为 0 或 1。你需要给一种指定点权的方案,使得每一条路径的点权 mexmex 之和最大。
n≤2×105n≤2×105,多组数据。
如果考虑跨越多种数的正贡献,发现很难。于是考虑块内的负贡献。
正难则反!!!!!!!!!!!!!!!!!!!!!!!哦
对于每一个连通块,考虑树形背包。
关键结论是连通块的大小一定不会很大,否则减少的贡献太多了。
有证明在 n≤2e5 小不会超过 258。
代码:https://codeforces.com/contest/1830/submission/222027041
E. Bully Sort
对于一个非升序的排列 {pi},定义一次操作为按顺序执行以下步骤:
-
令 pi 为排列中最大的满足 pi≠i 的数。
-
令 pj 为排列中最小的满足 i<j 的数。
-
交换 pi 和 pj
可以证明在排列非升序的前提下总能找到满足条件的 i 和 j。
定义一个排列的权值为将其升序排序所需的操作次数,可以证明总能在有限次操作内将其升序排序,注意如果排列本身就是升序的那么其权值为零。
现在给定一个排列和若干次修改操作,求出每次修改后排列的权值,每个修改操作为交换排列中指定的两个数。
注意修改是永久的。
发现排序方式和冒泡排序很像,于是考虑与逆序对之间的关系。
发现每次交换 (i,j) 使得逆序对的个数减少 2(j−i)−1。
发现 2(j−i) 这个东西很好,考虑交换会影响什么改变 2(j−i)。
发现 ∑ni=1|pi−i| 符合要求。
发现两者最后都为 0。
发现操作次数等于 ∑ni=1|pi−i| 与逆序对个数之差。
于是动态维护逆序对即可。
发现分块很优秀,O(n√nlogn),虽然是正常的 O(nlog2n) 的树套树的四倍时间……
代码:https://codeforces.com/contest/1830/submission/222012835
1836
A.Destroyer
开个桶记录个数,看满不满足单调不上升即可。
B.Astrophysicists
辛辛苦苦写了这么久的文章就没了????烦死了。
自己做 Virtual Contest 的时候这道题打表打了半天(
20min)才搞定……
题目大意
有 n 个人,k 个金币,每个金币价值 g 个银币。
良心公司要把这 k 个金币作为工资分给这 n 个人,但是没有办法平均分配,良心老板想出了分配规则:
-
由你设定每个人分配的银币数 xi。
-
你需要保证 ∑ni=1xi=k×g。
-
老板会把银币数转化为金币发放,所以想出了以下规则:
-
令 r=xmodg,如果 r≥⌈g2⌉,那么公司会额外花费 g−r 个银币以凑出完整的金币(此时花费了 x+g−r 个银币)。
-
反之,会吞掉这 r 个银币以凑出完整的金币(此时花费了 x−r 个银币)。
-
假定最终公司的花费为 p 个银币。你需要最小化 p,并输出 k×g−p。
解题思路
如果开始没有思路,那么打表是一个很好的方法。
首先我们可以很轻易的得到一个 O(nkg) 的 DP 算法。
设 fx,i 表示处理到第 x 个人,一共分配了 i 个银币公司省下的最多银币。
有转移方程:
其中:
运行后输出 DP 数组,结合 m=⌈g2⌉−1可以很轻易的发现规律:
最终的数组分为两个部分:
-
第一个部分为 [0,nm],下标与数值一一对应。
-
第二部分为 [−⌊g2⌋−m−nm,nm] 不断循环。
现在我们来严谨证明一下。
理想状态下,我们自然是给每一个人分发 m=⌈g2⌉−1 个银币,这样就可以吞掉 nm 个银币。
但是可能存在一下两种情况:
-
其实根本没有 nm 个银币,所以全部都分配到 ≤m 个。所以全部可以吞掉。对应上文中第一部分。
-
还剩下了 r=k×g−nm 个银币。由于我们可以按照一个金币为单位再分配,所以我们只需要关注 rmodg 的值。
显然,如果把这些银币分给不同的人是不优的,因为破坏了老板吞掉更多人 m 个银币的美梦。
所以这些银币应该全部分配给一个人,对于答案做出贡献 w(m+k×g−nm)−m。
(
化简一下)对应上文中第二部分。
提交记录:https://codeforces.com/contest/1836/submission/211134556
C.k-th equality
注意,注意:
Each input file has at most 5 test cases which do not satisfy A,B,C≤3.
所以,O(10min(A,B)) 可以过。
那么考虑顺序枚举 A 位的数 a,满足的数 b 应该为一个连续区间,这个可以 O(1) 解决。
所以区间长度与 k 判断一下即可。
提交记录:https://codeforces.com/contest/1836/submission/211137329
1835
B. Lottery
给定一个长度为 n 的序列 v,其值域为 [0,m]。
现在你需要选择一个 x∈[0,m],使其权值最大。定义一个数 x 的权值为:
请你找到权值最大的数 x,若有多个,输出最小的那个。
考虑 k=1 的特殊情况,发现两个人内部的所有点都造成了 12 的贡献。所以告诉我们不需要枚举太多的点,在一些点的周围枚举一下即可。
这可以很合理的外推到所有的 k 的情况。
C. Twin Clusters
给定 2k+1 个 [0,4k) 内的整数,请求出任意两个不交非空区间使得其异或和相等,若无解则输出 -1
。
抽屉原理抽象题。-1
是不可能的,这一定有解。
于是考虑生日悖论,随机化一些区间即可(逃。
考虑正解,将 4k 分成前 k 位和后 k 位。
前缀异或和有 2k+1+1 个,而前 k 位最多有 2k 种取值。所以至少会有 2k+1 对前 k 位相等的前缀。
考虑把这些对找出来,其后 k 位却也只有 2k 种取值,所以至少有两对的后 k 位的异或值相等。所以就完了。
代码:https://codeforces.com/contest/1835/submission/222050899
D. Doctor's Brown Hypothesis
给定一张 n 个点 m 条边的简单有向图和常数 k,请求出有多少组 (x,y) 满足 1≤x≤y≤n 且存在一条 x 到 y 和一条 y 到 x 的长为 k 的路径,k≥n3。
发现 n3,所以发现可以随便走环凑最小公约数。
于是先缩点,在连通块内考虑。
找出生成树,考虑一个非树边,有结论,d|abs(depy−depx−1)。
所以可以找出 d,而合法当 k≡0(modd) 或者 d≡0(mod2) and k≡d2(modd)。
用桶记录一下即可。
代码:Submission #222076413 - Codeforces
1827
A. Counting Orders
简单计数。
两个都排序,双指针维护一下 a[i]
在 b[p]
的位置(a[i] <= b[p]
)。
那么方案数 ×(p−i)。
提交:https://codeforces.com/contest/1827/submission/211387552
C. Palindrome Partition
回文好题。
考虑利用 Manacher
求出每一个偶数的回文串。
观察可以发现:每一个合法的串都可以被划分为多个最小的合法串,并且划分方法是唯一的。
所以考虑如下:
我们需要维护出每一个点作为右端点,最靠近的左端点。
这等价于求出满足 i+p[i]≥x,求 maxi。
-
做法1:利用上面的限制,可以通过
ST
表求解。 -
做法2:由于需要中心最大,所以逆序处理,利用每一个中心更新没有更新过的点。
由于每一个点只需要更新一次,考虑使用链表或者并查集维护。我用的是并查集。
然后,设上面对于端点 i 求出来的结果为 gi。
得出最终的递推式:
其中 fi 表示以 i 结尾的合法的串的个数。
最终的答案为 ∑fi。
提交:Submission #211396680 - Codeforces
D. Two Centroids
性质好题。
重心有如下性质:
-
最大子树大小 ≤⌊n2⌋。
-
新增一个叶子节点,重心最多变化一条边。
-
重心至多有两个。
那么我们考虑每次加入一个点对于原来重心 c 的影响,并设当前最小的子树大小为 mx。
-
如果加入的叶子节点在 c 的子树 t 内。更新 t 的大小,并更新 mx。如果此时 mx≥⌊n2⌋+1,那么重心向 t 移动。并且此时 mx 更新为 ⌊n2⌋(
不考虑,把图画出来就明白了。 -
由于我们不可能动态维护以每一个重心为根,所以还要考虑其祖先的情况(同理)。
然后就是如何找 t 的问题。有两种解决方案:
-
无论如何首先可以通过
dfn
序判断是否在其子树内。-
方案1:利用倍增的方法跳祖先。空间复杂度 O(nlogn),单次 O(logn)。
-
方案2:利用
dfn
区间,对于每一个节点保存其子节点的dfn
,每次二分找到叶子的dfn
所在的区间即可。(我用的这种方法)空间 O(n),单词 O(logn)。
-
-
实测第二种要快一点点(也快不了多少,主要是常数够好)
提交:Submission #211401150 - Codeforces
E. Bus Routes
差点没用 dlang 卡过去
思路1
考虑更严的限制(等价):叶子节点间两两可以在两次内到达。
于是考虑对于每一个叶节点,求出其能到达的最高的祖先 high(x)。
也就是求一个
LCA
即可。
那么满足条件的必要条件是每一对 high(x),high(y) 满足祖先/子节点关系。
如果不是,那么至少需要三条路径。
如果满足上述条件,那么所有的 high 满足在一条链上。
那么可以通过
dfn
序判断。
这并不充分。
考虑找到最低那个 high(x),并以 high(x) 作为根。
此时重新求一次 high,如果所有的 high 都为 high(x),那么就是 ok 的,否则,如果有 high(y)≠high(x),那么 x,y 不可达。
不要写倍增求 LCA,利用 ST表 O(nlogn)∼O(1) 或者 树链剖分 O(n)∼O(logn) ,甚至是离线 O(n) 求。倍增太慢了,dlang 卡不过去。
思路2
参考 Alex_Wei
的博客:https://www.luogu.com.cn/blog/_post/575397。
利用 DSU on tree
。
思路3
维护虚树并树上差分。非常的高效。
参考:CF1827E Bus Routes - Kubic 的博客 - 洛谷博客
1824
LuoTianyi and the Show
有 n 个人观看一场与 VOCALOID 有关的演出,场地里有一行座位,从左到右标号 1 到 m,接下来观众将按顺序以如下三种方式中的一种选择座位:
- 如果没人坐在座位上,就坐在座位 m 上,否则坐在目前最靠左的人的左边。若座位 1 有人,则离开。
- 如果没人坐在座位上,就坐在座位 1 上,否则坐在目前最靠右的人的右边。若座位 m 有人,则离开。
- 如果 xi 没人则坐在 xi,否则离开。
现在知道每个人选择座位的方式,你可以让人们按照任意顺序入场,求最多找到座位的人数。
安排方式只有三种,1/2/3 分别先,依次向左/向右/向两边扩展即可。
LuoTianyi and the Floating Islands
给定一棵 n 个结点的树,现在有 k(k≤n) 个结点上有人。
一个结点是好的当且仅当这个点到所有人的距离之和最小。
求在这 n 个点中随机取 k 个点时,好的结点的期望个数,对 109+7 取模。
发现用点来算很难算,于是考虑用边来算贡献。由于每种情况边会比点数少 1,所以期望点数等于期望边数 +1,于是考虑边如何做贡献,两边的点数相等即可。
于是就搞定了:https://codeforces.com/contest/1824/submission/222881710
LuoTianyi and XOR-Tree
给定一棵 n 个节点的树,根节点为 1 ,每个点有一个点权 ai ,每次操作可以选择一个点任意修改点权,求最少的操作次数,使得每一条根结点到叶子结点的路径上的异或和都为 0 。
如果要相等,那么只需要子树所有相等,那么改变只需要改变自己即可。
所以找到子树中最多的那个值,保留即可。
并且启发式合并即可……
代码:https://codeforces.com/contest/1824/submission/222885893
注意 if (maxcnt > 1)
的优化,否则会 TLE。
LuoTianyi and the Function
Feyn 给了你一个长度为 n 的整数数组 a,下标由 1 开始。
用以下方式定义函数 g(i,j):
- 当 i≤j 时,g(i,j) 是满足 ap:i≤p≤j⊆aq:x≤q≤j 的最大整数 x;
- 否则 g(i,j)=0。
q 次询问,每次给定 l,r,x,y,请求出 r∑i=ly∑j=xg(i,j) 的值。
需要个可以区间赋值和历史版本求和线段树……不会额。
↓2023.09.11
1819
A. Constructive Problem
给定整数 n 和一个非负整数序列 a。
你需要选择整数 l,r,k(1≤l≤r≤n;0≤k),然后将 al,al+1,⋯,ar 的值均变为 k。
判断能否通过恰好一次上述操作使操作后的 mex(a) 增加 1。
如果没有 mex(a)+1 的存在,并且 mex(a)<n,那么一定存在方案。
否则,需要变换所有的 mex(a)+1,而为了使影响最小,只是恰好包含所有的区间变成 mex(a),重新求一次 mex 即可。
代码:https://codeforces.com/contest/1819/submission/222839488
B. The Butcher
有一张 h×w 的纸片,但你并不知道 h 与 w 的具体数值。现在对这张纸片进行 n−1 次裁剪。每次裁剪后会将其中一半收归(即这一半不会再被裁剪),保证纸片不会被旋转。
告诉你最终裁剪后的 n 张纸片长宽,问初始有多少 h×w 的纸片可以裁剪成这 n 张纸片,输出所有可行的 (h,w)。
发现一张一定是 h 或者 w,那么找到 maxh 和 maxw 作为两种可能即可。
由于每次减下 w,h 递减,所以考虑双指针扫一遍即可。
代码:https://codeforces.com/contest/1819/submission/222845297
C. The Fox and the Complete Tree Traversal
给定整数 n 和一棵包含 n 个节点的树。
记 Dist(x,y) 表示树上节点 x,y 之间最短路径的边数。
你需要判断是否存在一个 1∼n 的排列 p,满足:
- Dist(pi,pi+1)≤2 对任意整数 i(1≤i<n) 成立。
- Dist(p1,pn)≤2。
存在则输出 Yes
然后输出任意一个满足要求的 p,不存在则输出 No
。
有两种方法,一种是我考场想出来的,分讨比较严重:
先考虑状态 fx,0/1 表示走完其子树后是否可以在其子节点或者本身上。
发现叶子节点对于其可行性没有影响,考虑非叶节点的影响。
不难分类讨论,设当且节点为 x:
- 如果没有非叶节点,那么不难发现最后无论在哪里都可以。
- 如果只有一个非叶节点 y,那么发现 fx,0 满足当且仅当 fy,1 满足,同理,fx,1 满足当 fy,1 满足。
- 如果有两个非叶节点,发现除非这个点是根,或者父节点为根且根没有其他子树,否则绝对无法满足。而如果满足上述条件,那么最开始无论是从 x 出发,还是最终到达 x 本质相同。
所以我们可以愉快的 DP 了,但是考虑父节点为根且根没有其他子树的情况不太好判断,所以任意找一个度数 ≥2 的点作为根,那么自然这个条件就无法满足,如果没有找到……这还是能一颗树?
int rt = 1;
void dfs(int x, int p) {
dep[x] = dep[p] + 1;
int son = 0;
vector<int> nonleaf;
for (int y : G[x]) {
if (y == p) continue;
++son;
dfs(y, x);
if (!leaf[y]) nonleaf.push_back(y);
}
if (son == 0)
leaf[x] = 1, f[x][1] = 1;
else if (nonleaf.size() == 0)
f[x][0] = f[x][1] = 1;
else if (nonleaf.size() == 1) {
int y = nonleaf[0];
f[x][0] = f[y][1], f[x][1] = f[y][0];
} else if (x == rt && nonleaf.size() == 2) {
int y = nonleaf[0], z = nonleaf[1];
if ((f[y][0] && f[z][1]) || (f[y][1] && f[z][0])) f[x][1] = true;
}
}
这样,从根开始,依据 DP 转移的过程构造即可。
vector<int> v;
void construct(int x, int t, int p) {
vector<int> nonleaf;
for (int y : G[x]) {
if (y != p && !leaf[y]) nonleaf.push_back(y);
}
#define pushLeaf() { for (int u : G[x]) if (u != p && leaf[u]) v.push_back(u); }
if (nonleaf.size() == 0) {
if (t == 0) v.push_back(x);
for (int y : G[x]) {
if (y != p) v.push_back(y);
} if (t == 1) v.push_back(x);
} else if (nonleaf.size() == 1) {
int y = nonleaf[0];
if (t == 0) {
v.push_back(x);
construct(y, 1, x);
pushLeaf();
} else {
pushLeaf();
construct(y, 0, x);
v.push_back(x);
}
} else if (nonleaf.size() == 2) {
int y = nonleaf[0], z = nonleaf[1];
v.push_back(x);
if (f[y][1] && f[z][0]) {
construct(y, 1, x);
pushLeaf();
construct(z, 0, x);
} else if (f[y][0] && f[z][1]) {
construct(z, 1, x);
pushLeaf();
construct(y, 0, x);
} else assert(nonleaf.size() == 2);
}
}
虽然是说稍微复杂了一点,但是分讨很简单,代码依据分讨就可以直接模拟出来,十分的 naive
,这比正解要更 brute force
一点(CF优良传统)。
而对于正解,先找出最长链,那么支链的深度不能超过 1,否则一定不合法,O(n) 做即可,只是要扫很多遍而已。
D. Misha and Apples
给定 n 个集合 Si,第 i 个集合的大小为 ki,集合元素为 1∼m 的正整数。特别地,若 ki=0,则 Si 可以是正整数 1∼m 的任意可空子集,由你确定。
设 可重集 S,初始为空。按编号从小到大依次遍历每个集合,往 S 中加入 Si 所有元素。每次加入当前集合的所有元素后,若 S 包含重复元素,则清空 S。注意,一个集合内的元素 同时 被加入 S。
你需要确定 ki=0 的 Si 具体包含哪些数,使得最终的 |S| 最大。求出这个最大值。
1≤T,∑n,m≤2×105,0≤∑ki≤2×105,Si 的元素互不相同。
考虑找到可以清空的位置 cani,以及每一个 i 往前最多可以保留到的位置 late。
每次找到最后一个冲突的位置 max_conflict 记为 mc,于是如果 mc≤late 那么对于 late 没有影响,而 cani 可行当且仅当 (late,i] 之间存在一个 k=0。
否则 cani 一定可行,而 late 需要更新到 mc 之后第一个 cani 为 1 的点。
于是,就搞定了。
E. Roads in E City
这是一道交互题。
给定一张 n 个点 m 条边的无向连通图,无自环,但可能有重边。
图上有一些特殊边,保证任意两个结点通过特殊边连通,你希望求出所有特殊边。
初始所有边都没有被堵住。你可以进行以下三种操作:
- − x(1≤x≤m):如果第 x 条边没有被堵住,则堵住该边。
- + x(1≤x≤m):取消堵住第 x 条边。操作前第 x 条边必须被堵住。
- ? y(1≤y≤n):交互库随机选择某个 s 并返回能否从 s 出发走没有被堵住的特殊边到 y。
你最多进行 100m 次操作。
2≤n≤2000,n−1≤m≤2000,1≤t≤1000,∑n,∑m≤2000。保证图无自环且连通。
利用其随机化,以及联通的性质。
找到一个特殊的生成树,然后考虑非树边即可。
问题在于如何判断树边,断开,然后两边跑 k 次,那么错误率为 (xnn−xn)k,在 k→20 的时候就趋近于 0 了,可以认为完全正确。
于是就做完了。
↓2023.09.18
1817
A. Almost Increasing Subsequence
给定长度为 n 一个序列 a 以及 q 次询问,每次询问给出 l 和 r,找出序列 a 在 [l,r] 内最长的几乎递增子序列。
对于几乎递增的定义:如果一个序列中不存在连续的三个数 x,y,z,使得 x≥y≥ z,则这个序列是几乎递增的。
也就是说每一段递减的都至多取两个。转换一下,减去连续的三个的个数,差分一下即可。
代码:https://codeforces.com/problemset/submission/1817/223787996
B. Fish Graph
定义“鱼图”为满足以下条件的无向图:
- 该图包含正好 1 个环,环上有 1 个特殊的结点 u,u 除了连在环上的 2 条边外还有正好 2 条边,每一条边连向一个环外的结点(即,若环的大小为 s,整个图一共有 s+2 个结点)。
现在给定一个简单无向图,问该图中是否有一个子图为“鱼图”。一个无向图的子图即为原图中删去若干个点和若干条边所形成的图。如果存在,还需要构造出其中任意一个满足要求的子图。
合法的点当且仅当 Dx≥4 并且 SCCx≥3,于是可以 O(n) 求了。
注意我的写法中,需要回退,防止 <><> 的这种图找不到环。
代码:https://codeforces.com/problemset/submission/1817/223785932
C. Similar Polynomials
给定两个次数为 d 的多项式 A,B 在 0,1,2,…,d 处的点值对 109+7 取模,保证 B(x)≡A(x+s)(mod109+7)。求 smod109+7。
考虑差分:
于是可以求出 s 了。
还有拉格朗日插值求出系数然后求的方法,差不多。
代码:https://codeforces.com/problemset/submission/1817/223798973
D. Toy Machine
有一个长 n 宽 2 的矩形游戏机。保证 n 是奇数。
游戏开始之前,有 n−2 个玩具放在第一排中间的 n−2 个位置中(两个角除外)。编号从左到右依次为 1 到 n−2。第二行为空,其最左、最右和中心位置是障碍物。你可以控制玩具机向左、向右、向上、向下,分别用字母 L、R、U 和 D 表示。
操控时,所有玩具将同时沿相应方向移动,碰到另一个玩具、墙壁或障碍物停止。您的目标是将第 k 个玩具移动到第一行最左边。给定 n 和 k,构造一个最多使用 1000000 次操作的方案。
认为代码展示了一切:https://codeforces.com/problemset/submission/1817/223803543
E. Half-sum
有一个非负整数序列 {a1,a2,…,an}。
你可以从序列中取任意两个数,并将它们的平均数放回序列。
序列最后会剩两个数,请求出这两个数的差的绝对值的最大值。
因为答案将会是一个小数,所以请输出答案对 109+7 取模的结果。
呃,推一点点式子,发现如果从断点 x 到断点 y 的差距为:
于是可以 O(y−x+1) 求解,于是发现可以分治求解。
问题在于如何维护这些加加减减的值。
有一个 trick
:Trygub Number,适用于此。
于是可以 O(nlogn) 求解了。
注意可能需要较快的读入:Submission #223847308 - Codeforces
然而正解,考虑 2i 在 i≥30 时便很大了,使得 ai2i 趋近于 0,于是可以发现断点只会出现在前/后 logn 个不同的数上,于是常数为 64 的 O(n) 做法新鲜出炉!
1815
Div. 1
确实难,Virtual Contest
上只完成了两道题,想出来了三道题。
A. Ian and Array Sorting
秒切题……考虑将前 n−1 个数变成一样的一个数 x。显然可以完成。
然而考虑此时最后一个数。如果 ≥x,那么是 \colorbox{#52C41A}{\textcolor{white}{AC}},如果 <x,要分类讨论:
-
如果前面有奇数个,是 \colorbox{#E74C3C}{\textcolor{white}{WA}}。
-
反之,则是 \colorbox{#52C41A}{\textcolor{white}{AC}}。
提交:Submission #211291004 - Codeforces
B. Sum Graph
傻逼交互。
好呀好呀,傻逼交互体。想出来了思路,但是就是没有写出来。
考虑 +
点 n+1,n+2。那么整个图会形成一条链。
static int perm[N]; int l = 1, r = n;
for (int i = 1; i <= n; ++i) {
perm[i] = (i & 1) ? l++ : r--;
}
于是我们可以在 n−1 次以内找到链的一端,然后就可以再来 n−1 次找到可能的排列。
由于我们可能是找到的两端,所以有两种排列方法。
输出即可。
提交:Submission #211296415 - Codeforces
进阶:参考 https://codeforces.com/blog/entry/114847?#comment-1021812 以及 Editorial of Codeforces Round #865 - Codeforces,可以把次数减少到 n+⌊n2⌋+2 次询问。
C. Between
构造好题。
考虑包含可以形成依赖。
于是可以形成一张图。
我们从 1
开始 BFS
对其分层,可以发现,第 i 层最多可以放 i 个(包含依赖多套一个)。
那么考虑如何构造合法方案。
我的方法是每一次,按照 BFS
逆序遍历很多遍,每一次遍历到,如果还有没放的则放一个,否则略过。(好像官方题解就是这样?看不懂)
考虑正确性:按照逆序遍历保证出现次数少的在中间,保证不同层之间的依赖关系;每次放一个保证出现次数相同的交替出现,这样就保证了层内的依赖关系无论如何一定满足(互相依赖也没关系)。
提交:Submission #211298131 - Codeforces
D. XOR Counting
打表好题。
在 m=1 的时候答案就是 n。
在 m>3 的时候小小打表,可以发现答案为 ⌈n2⌉×(⌊n2⌋+1)。
证明:在 m>3 的时候,[x,n−x2,n−x2,0,0,…] 可以恰好凑出 x,所以所有与 n 奇偶性相同的 x(此时 n−x 为偶数,n−x2 为整数)都可以被凑出。
然后我们发现,加法和异或得到的奇偶性是一样的,并且有 a⊗b≤a+b,意味着我们只能凑 ≤n 并且与 n 奇偶性相同的数。
综合两条,我们便可以得出,所有可能凑出的数就是 ≤n 且与 n 奇偶性相同的数。
推一下公式就是上面的了。
于是问题就只剩下了 m=2 的时候。
我们考虑一个 DP,我们记 f(n) 表示所有可能的异或和的和,g(n) 表示可能的个数。然后稍微分类讨论一下:
-
如果 n 是奇数,我们不妨假设 n=a1+a2,其中 a1 为偶数,a2 为奇数。设 a′1=a12,a′2=a2−12。我们可以看出:a′1+a′2=n−12 以及 a1⊗a2=2×(a′1⊗a′2)+1。此时个数不会变,而和变为原本的两倍再加上个数。也就是 g(n)=g(n−12) 以及 f(n)=2f(n−12)+g(n−12)。
-
如果 n 是偶数,那么我们又要分两类讨论:
-
设 n=a1+a2,其中两者都是偶数,那么类似的,a′1+a′2=n2,a1⊗a2=2×(a′1⊗a′2)。
-
如果都是奇数,有 a′1+a′2=n2−1 以及 a1⊗a2=2×(a′1⊗a′2)。
-
因此,我们可以知道 f(n)=2×f(n2)+2×f(n2−1),以及 g(n)=g(n2)+g(n2−1)。
-
FAKE: 于是我们可以得出递推复杂度 T(n)=T(n2)+1,得出为 O(logn)。
依据SMB,复杂度似乎不是这样的,但是题解是这么给的,或许是有一些神秘的分析,或者是加了记忆化,所以复杂度是这样的。
其实完全可以用记忆化……快一点吧。
提交:https://codeforces.com/contest/1815/submission/211297560
E. Bosco and Particle
性质好题。
性质:
-
循环多次 = 循环一次
-
设 ai 表示实际上从上面进入这个序列的次数,bi 表示实际上向下面出去这个序列的次数。我们把第 i 个序列单独提出来,类似的定义 a′i 和 b′i。可以发现,∃k⟹ai=ka′i,bi=kb′i,也就是有 aibi=a′ib′i
-
有 ai=bi−1。
-
令 fi=ai+1,f0=a1,于是有 fk=∏ki=1biaif0。
-
需要保证 ∀k,fk 为整数。
于是就可以写出来啦!!!(不是
提交:Submission #211323975 - Codeforces
竟然比
C++
快,不可思议
1868
A. 2D Traveling
该题共有 t 组数据,对于每一组数据,给定一个共有 n 个节点的图,其中有 k 个关键点(两个关键点之间移动花费为0)。给出 n 个点的坐标 (xi,yi) ,求从 a 到 b 两点之间的最小移动花费。
除关键点以外,两点之间的移动花费为曼哈顿距离。
策略要么是 a 直接到 b,要么是 a 先到最近的关键点,跳到 b 最近的关键点。
直接做即可,代码:https://codeforces.com/contest/1869/submission/223695140
B1/2. Candy Party
有 n 个人,第 i 个人有 ai 颗糖,在派对上,每个人 会且仅会做下面的事情恰好一次 :
- 选一个正整数 p ( 1≤p≤n ) 和一个非负整数 x ,然后把 2x 颗糖给第 p 个人。注意任意时刻一个人手上的糖不能变成负数,并且一个人不能把糖给自己。
你需要回答能否在上述操作后让每个人手中的糖果数量相同。
注意本题和 Hard Version 不同的是本题中每个人必须从他人处接受恰好一次糖果,给出恰好一次糖果。
也就是每一个人要么使用 2x−2y,要么使用 2x,要么使用 22x−2x。分开考虑来 DP 即可。
C. Travel Plan
给定一颗 n 个节点的完全二叉树,每个点有权值 ai∈[1,m],定义从 i 到 j 的路径的权值 si,j 为路径上的最大点权。
求所有树(mn 种点权)的 ∑ni=1∑nj=isi,j 的和,模 998244353。
很显然,我们可以分别求出每种长度路径对答案的贡献,然后再在树上统计各个长度路径的个数,最后把贡献加起来即为答案。
D. Flower-like Pseudotree
给你一个基环树,我们称一个基环树是像花的当且仅当删去基环树中的环后,剩余的所有树高度一致,请你构造一个像花的基环树,满足树中所有点的度数为 di,
大力分讨即可。
1863
A. Channel
没什么好说的,模拟即可。
代码:https://codeforces.com/problemset/submission/1863/225013381
B. Split Sort
给定一个 1∼n 的排列 q,你可以多次进行以下操作:
- 新建一个初始为空的序列 q;
- 选择一个整数 x(2≤x≤n);
- 按照在 p 中出现的顺序将所有小于 x 的数添加到序列 q 末尾。
- 按照在 p 中出现的顺序将所有大于等于 x 的数添加到序列 q 末尾。
- 用序列 q 替代排列 p。
你需要找到使 ∀i∈[1,n],pi=i 的最小操作次数。
本题有多组测试数据,1≤T≤103,1≤n,∑n≤105。
记 i 出现的位置为 pi,则答案为 ∑ni=2[pi<pi−1]
代码:Submission #225039544 - Codeforces
C. MEX Repetition
给定值域在 [0,n] 的一个序列,pi 互不相同,定义一次操作为 i=1→n,pi=mex(P),求 k≤109 后的序列是什么样子的。
将初始的 mex 放在序列开头,则一次操作相当于 i=1→n,swap(p0,pi),结果是加入了 mex 的序列向右循环了一次。
所以最后就是循环 k 次,去掉第一个数即可。
代码:https://codeforces.com/problemset/submission/1863/225014759
D. Two-Colored Dominoes
有一个 n×m 的棋盘,上面铺着一些 1×2 的多米诺骨牌(横竖均有可能),骨牌之间没有重叠。
你需要找到一种染色方案满足以下条件:
- 每个多米诺骨牌一端被染白,另一端被染黑。其他没有骨牌的格子不染色。
- 对于棋盘的每一行,被染黑的格子数等于被染白的格子数。
- 对于棋盘的每一列,被染黑的格子数等于被染白的格子数。
请输出任意一种染色方案,如果无解,输出 −1。
本题有多组测试数据,1≤T≤104,2≤n,m≤500,∑(n×m)≤2.5×105。
发现横竖之间没有影响,所以分开考虑。
考虑只有竖着,那么横向黑白染色,这无法影响道下一行,所以忽略。
纵向类似即可。
代码:https://codeforces.com/problemset/submission/1863/225041894
E. Speedrun
你在玩一个游戏,要完成 n 个任务。其中对于每个任务 i,它只能在某一天的第 hi 时刻完成。游戏每天有 k 个小时,分别编号为 0,1,...k−1。
给出 m 对任务间的依赖关系,(ai,bi) 表示 ai 必须比 bi 先完成。保证依赖关系不形成环。
完成任务不需要时间,也就是说可以在同一天的同一时刻先后完成多个任务。
求完成所有任务所需的最短时间。这里的时间定义为:完成最后一个任务的时刻 与 开始第一个任务的时刻 之差。
多组数据,T≤105,∑n,m≤2×105,k≤109。
最优的情况是延迟了一部分拓扑序第一的点,而且这一部分一定是前缀。
所以扫一次前缀,一一延迟更新当前答案,然后更新总答案。
每一个点至多更新一次,所以这部分 O(n)。
代码:https://codeforces.com/problemset/submission/1863/225021336
F. Divide, XOR, and Conquer
有一个长为 n 的数组 a1,a2,⋯,an。
在每次操作中,你可以把数组分成两段,留下一段,扔掉另一段,要求前者的异或和不小于后者的。重复操作,直到只剩下一个数为止。
对于每个 i,问最后剩下来的可不可能是第 i 个数。
有 t 组数据。1≤t≤10 000,1≤n≤10 000,1≤ai≤260,∑n≤10 000。
有一个显然的 O(n3) 做法。
考虑一个区间 [l,r] 什么时候可以被转移过来,由于异或,从二进制入手。
假定从 [l,k] 转移过来,其区间异或和为 x,[l,r] 区间异或和为 y。
如果 x 的第 k 位为 0,那么显然两边这一位都是相等的。如果为 1,那么有一边会大一点,下面的位随意了。
所以可以有条件 y&highbit(x)≠0 则可以转移。
于是一个一个长度递减做下去即可。
代码:https://codeforces.com/problemset/submission/1863/225050040
G. Swaps
给定长度为 n 的序列 a,每次操作可以选定一个 i,并 swap(ai,aai)。求能通过某种操作顺序得到的不同序列数。
n≤106。
简洁的题面,深邃的思想。
首先,一个经典的套路是:
对于序列中涉及到对于 ai 和 aai 进行操作的问题,一般可以考虑建立 (i,ai) 的内向基环树或者 (ai,i) 的外向基环树转化为图论问题。
-- from weakpyt
一次交换相当于使得一个点自环,儿子连向父亲。
于是转换为对于一个点,儿子可以断一条边,并且至多段一条边的模型。
所以如果只是一棵树的话,答案就是 ∏(dx+1)。
然而有环,发现环上所有边的断掉的情况不存在。
再发现环上留一条边不断,但是每一个点都断了一条边/或者不变的情况都是一样的,这部分显然是 ∑(dx−1+1) 的。
所以减去重复的,只保留一个,答案为:
化简一下:
这样就好求了。
代码:https://codeforces.com/problemset/submission/1863/225037149
H. Goldberg Machine
给定一棵树,每个节点要么没有儿子,要么有两个儿子,每个叶子节点有一个权值。
多次修改某个叶子节点的权值,维护递推式子:fx=2max(fy,fz)−(fy≠fz)。
令 tx=fx−1,则递推式子转换为 tx=2max(ty,tz)+[ty=yz]。
记 x 的深度为 depx,则可以令叶子 gx=2depxfx,则递推式子为 gx=max(gy,gz)+2depx[gy=gz]。
有一个结论是 gx 的二进制中 1 的个数是 logA+logn 级别的。
考虑暴力合并。
假定 x,y 的 g 相等,则可以在 z=LCA(x,y) 处贡献 2depz。
于是可以利用 vector
字典序暴力找相同的,然后合并即可。
最后会得到很多个答案,取最大的即可。
为什么取最大的正确性没问题?考虑如果 x 在和 y 之前与某一个 u 合并了,那么合并的位置一定在 z 下面,也就是贡献 2dep 一定大于 2depx,使得最终的答案大于错误的答案,所以如此是没有问题的。
由于当日晚上呕吐,身体不适,所以没有写本题。然而代码实际上是好写的,不过多赘述。
1753
成功因为虚拟机炸了,重新写一遍此文。
都是没有保存的错。
A. Make Nonzero Sum
由于 Note that it is not required to minimize the number of segments in the partition.
。考虑每一段最小化……
可以发现,每一段都可以划分为长度为 1 或 2 的段。于是考虑影响。
只有长度为 2 的段会改变正负,不妨令 C+,C− 分别表示 1 和 -1 的个数,并假定 1 更多。
不难发现,只需要 |C+−C−|2 个长度为 2 的即可。
如果不是整数,那么直接判不可以即可。
由于有影响,考虑 DP。
设 fi 表示考虑前 i 个数,最多能够放多少个长度为 2 的。
于是有
考虑在 DP 变化的地方放置长度为 2 的即可。
B. Factorial Divisibility
当时脑子抽了,用了两种合并的方法。
但是实际上只需要通过 x!=x×(x−1)! 合成即可(2048……
C. Wish I Knew How to Sort
假定有 C0 个 0,并且在前 C0 个数中有 k 个 1。
那么考虑此时一个有效的操作,即是在前 C0 中选择到了一个 1,在后面中选择了一个 0。
有效的概率为
于是考虑状态转移,设 fk 表示从前 C0 个数中有 k 个 1 的状态转移到 0 个 1 的期望步数。
根据 markov 中的期望线性方程求解的方法,有
稍微魔改一下,就变成了:
于是小小递推即可。
然而我当时是反着推的,无所谓,一样的:Submission #211560140 - Codeforces
D. The Beach
转换问题:等价于将两个 .
移动到一起的最小代价。
显然可以发现,一个障碍最多移动一次。
借用大佬的图:
于是我们可以考虑如此建图。跑一个最短路即可。
提交:Submission #211566195 - Codeforces
E. N Machines
非常恶心,虽然不是顶级难度。
最优的策略一定是把乘法向后移,把加法向前移。
思考 It's guaranteed that the current value of the resulting product does not exceed 2x10^9.
的意义。
发现,除去 ×1,最多只会有 logC 个乘法。
于是考虑枚举其子集,为 2logC。所以需要优化。
有一个简单而优雅的剪枝:如果两个数相等,那么一定是选择最前面的。
由于 12!=6227020800>2×109,所以其实最多只会有 O(212) 种状态。
那么在钦定了向后移动的乘法后,我们需要找到前 rest 个移动到前面贡献最大的加法。
考虑二分移动到前面的贡献 Δ,在每一段再二分数量。
考虑如何计算每一个加法的 Δ ?考虑加法移动前,其贡献为 x×sufx,移动后的贡献为 x×prex×sufx。
其中 sufx 和 prex 是指乘法移动后,x 前面的乘法前缀积和后面的乘法后缀积。
于是 Δx=x×(prex−1)×sufx。
NOTICE:
-
二分 Δ 时找到最大的 cnt>rest 的那个 Δ,由于多算了 cnt−rest 个,并且这些数的贡献一定是 Δ,所以再减去 (cnt−rest)×Δ 即可。
-
Δ 可能很大很大,所以上界大一点(我用的倍增,所以直接是从 260 开始向下……虽然没必要)
提交:Submission #211609810 - Codeforces
F. Minecraft Series
首先固定一个正方形,考虑贡献:将数分为正数与负数,分别计算 mexp 与 mexn。
正的为
positive
,负的为negative
。
于是贡献为 mexp+mexn−1。
由于 mex 的单调性,发现包括合法正方形的正方形一定合法,所以考虑双指针维护所在最小合法正方形的。
注意,是在每一条对角线上来一发双指针,这样才能保证复杂度。
然后,然后就搞定了。
可以优化的是,mex 可以利用分块优化复杂度。
于是你可以得到一个复杂度为:
的优雅 brute force
……
提交:https://codeforces.com/contest/1753/submission/211685792
然而……不断的 TLE 让我怀疑人生,最后发现……
参考:讨论
1718
A. Burenka and Traditions (hard version)
有 T 组数据,对于每一组数据,你有一个长度为 n 的数组 a,每一次操作可以选择一段区间 [l,r],花费 ⌈r−l+12⌉ 秒使区间内的数都异或 x,问最少需要几秒才能把数组中所有的元素都变成零。
首先不难发现有一个非常简单的上界 n,也就是每两个两个的变成 0 即可,或者换成一个一个向后推的过程。
然而如果存在一个区间本身异或和为 0,那么发现左端点是没有必要推的,这使得操作次数 −1。发现这优化十分显然,于是利用 set
维护最小的段即可。
代码:Easy: https://codeforces.com/problemset/submission/1718/229358204, Hard: https://codeforces.com/problemset/submission/1718/229358185
B. Fibonacci Strings
给你一个数列 {ck},可以执行若干轮操作(轮数由你决定)。对于第 i 轮操作:
-
选定一个 [1,k] 范围内的整数 di。当 i≥2 时,必须保证 di≠di−1。
-
将 cdi 减去 fi,其中 fi 是斐波那契数列(1,1,2,3,5,8,13,…)的第 i 项。
问:你能否让 c1 至 ck 全为 0?
呃呃,原题不是这样的,但是既然这么翻译了,那么贪心的模拟一下即可。
代码:https://codeforces.com/problemset/submission/1718/229357931
C. Tonya and Burenka-179
给定一个长度为 n 的序列,要求求出一个权值最大的对 (p,k),其中 0≤p,k<n。
定义一个对 (p,k) 的权值为 ∑ni=1Ap+kimodn。
先考虑最朴素的做法 O(n2),发现,考虑优化其过程,发现可以类似于分治,例如 (p,k) 可以由 (p,2k) 和 (p+k,2k) 合并而来。
但是发现 (p,k) 一定不必 max((p,2k),(p+k,2k)) 更优,所以此时 k 是不必要的。
于是发现 k 是最优的当且仅当 nk 为质数,这只有 6 个,每一个用一个 set
维护即可,这是 O(6nlogn) 的。
代码:https://codeforces.com/problemset/submission/1718/229362201
D. Permutation for Burenka
给定一个排列 p 和有 k 个空位的序列 a,已经确定了可以 k−1 个数,每次询问给定一个数,要求是否可以将这 k 个数填入 a 中使得 ∀i<j,argmaxp[i,j]=argmaxa[i,j]
发现这个限制可以转化为两者的笛卡尔树同构,发现排列的笛卡尔树是确定的,先构出来,然后考虑 a。
发现每一个空位都可以填的数是一个区间,而剩下一个数合法也一定是一个区间,于是考虑如何求上下界。
首先将每一个位置的上下界求出来,考虑一个十分经典的贪心来求它,按照区间右端点从大到小排序,尽量选择大的来满足它,那么到第一个不可以填的区间的时候,就意味着剩下的一个数必须大于这个区间的左端点了,于是可以求出下界了。
同理,按照左端点从小到大排序,可以求出其上界。
不过需要注意,如果遇到了两个没有办法满足的区间的时候,无论填什么都不可以了,此时所有询问回答皆为 NO
。
代码:https://codeforces.com/problemset/submission/1718/229374552
E. Impressionism
Burenka 有两张图片 a 和 b ,它们的大小可以表示为 n×m 的像素组合。每幅画的每个像素都有一个颜色——表示为一个从 0 到 2×105 的数字,并且在两幅画的每一行或每一列中都没有重复的颜色,除了颜色 0 。
Burenka 想把图片 a 变成图片 b。为了实现她的目标,Burenka 可以执行 2 操作之一:交换 a 的任意两行或其任意两列。现在需要你来告诉 Burenka 该如何操作,或者说明其不可能完成。
模拟 PityProblem,利用二分图一一匹配即可。
F. Burenka, an Array and Queries
每次询问一个区间,求 ∏ai 与 1∼C 中互质的数的个数。
有根号分治,或者乱搞。
但是官方题解所说:The only one hint to this problem is don't try to solve, it's not worth it.
1710
B. Rain
给定一个 n ,这 n 天会降雨,每一个降雨天有一个 xi 和一个 pi ,表示以 xi 为中心,降雨量向左右两边递减,即对于所有的数轴上的点 x ,其降雨量都会增加 max(0,pi−∣xi−x∣)
现在你可以消除一天的降雨,问消除第 i 天的降雨是否可以使得没有任何地方的降雨大于 m。
考虑整个图长什么样子,发现峰值(很多个)一定是某个 xi 上。
于是求出每一个 xi 对应的降雨 hi,这可以通过二分和差分很容易的求出(考虑每一次增加的是一个一次函数,这可以通过 k,b 简单的维护)
于是考虑对于删除一个后 m 的限制。
如果 hi≤m,自然不需要关注,删除一个 k 之后 hi→hi−max(0,pk−|xk−xi|)。
我们需要 hi−max(0,pk−|xk−xi|)≤m 也就是 h−(pk−|xk−xi|)≤m,考虑原本 hi>m,如果后者不满足,那么必定不满足。
于是就是 hi−pk≤m+|xk−xi|,考虑绝对值又是 max(xk−xi,xi−xk),于是两边同时都需要满足,也就是:
分别记录 hi+xi 和 hi−xi 的最大值即可判断 k 删除后是否满足条件。
代码(我在乱搞……所以代码有些抽象):https://codeforces.com/problemset/submission/1710/229413132
C. XOR Triangle
给你一个数 n,问:有多少对数 0≤a,b,c≤n 满足 a⊕b,b⊕c,a⊕c 。三个数字构成了一个非退化三角形,也就是两条短边之和大于第三边的长度。⊕ 表示二进制下的异或操作。
首先,很难发现对于所有数位,如果 a⊕b+b⊕c>a⊕c,那么就满足 x⊕y+y⊕z>x⊕z。其余两个限制同理。
于是考虑数位 DP,压位判断是否满足三个条件即可。
代码:https://codeforces.com/contest/1710/submission/229415904
D. Recover the Tree
你需要根据题目给出的信息构造出一棵树,满足如下条件:
- 树的节点个数为 n。
- 对于每个区间 [l,r] 给出是或不是连通块。
数据保证有解,n⩽2000。
首先满足小的区间是最优的,考虑如何满足大的区间。
考虑合并 i,j,那么此时的联通块大概是这样的:
按照上面的连边即可,注意是连到 i 和 j,而不是 i,j 所在连通块的某一个点,中间的块随意,利用并查集维护即可。
代码:https://codeforces.com/contest/1710/submission/229423482
E. Two Arrays
现有两个整数数组 a1,a2,…,an 和 b1,b2,…,bm。
Alice 和 Bob 将要玩一个游戏,Alice 先手,然后他们轮流进行操作。
他们在一个 n×m 的网格上进行游戏(网格有 n 排 m 列)。刚开始,有一个棋子放在网格的一排一列上。
在 Alice 或 Bob 轮次中,玩家可以选择以下两个动作中的一个进行操作:
- 将棋子移动到一个不同的格子上,该格子必须和棋子的原位置在同排或者同列上。玩家不能将棋子移动到已经被访问过 1000 次的格子上(也就是说,在游戏过程中,棋子最多可以在某个格子停留 1000 次)。请注意,我们规定起始单元格在开始时被视作访问过一次。
- 以 ar+bc 的得分立刻结束游戏,(r,c) 表示棋子当前所在的单元格(也就是说,棋子在第 r 排第 c 列上)。
Bob 想要最大化自己的得分,Alice 则想要最小化自己的得分。如果他们都以最佳方式玩这个游戏,则最终的得分是多少?
其实发现 1000 次和 1 次没有区别,直接忽略。
首先,我们知道博弈双方足够聪明,可以直接知道比赛结果 res,那么意味着 Bob 只能走大于 res 的地方,而 Alice 只能走小于等于 res 的地方。
于是可以得到一张二分图,这让我们想到经典的结论:Alice 赢的充要条件是二分图的最大匹配必须包含起点 S。
但是边数是 O(nm) 的,非常难受,考虑转化思路,找二分图最大独立集,这要求每行每列都只有一边出现,我们给每个行列涂颜色。那么一个方格可以取当且仅当方格的颜色和行列所染颜色都一样。
于是对于整个图(a,b)排个序,于是发现黑白大概就是如图:
枚举这个点即可,但是直接枚举是不可以接受的,这是 O(nm),但是考虑到这必定是一个凸函数,所以可以单调性优化,可以做到 O(n+m) 找。
于是加上二分的复杂度为 O(nlogn)。
1707
A. Doremy's IQ
哆来咪·苏伊特参加了 n 场比赛。 比赛 i 只能在第 i 天进行。比赛 i 的难度为 ai。最初,哆来咪的 IQ 为 q 。 在第 i 天,哆来咪将选择是否参加比赛 i。只有当她当前的 IQ 大于 0 时,她才能参加比赛。
如果哆来咪选择在第 i 天参加比赛 i,则会发生以下情况:
- 如果 ai>q,哆来咪会觉得自己不够聪明,所以 q 将会减 1;
- 否则,什么都不会改变。
如果她选择不参加比赛,一切都不会改变。哆来咪想参加尽可能多的比赛。请给哆来咪一个解决方案。
- 简单二分,前面部分只参加 ≤q 的比赛,后面部分经量参加,看是否可是使得 q=0。
- 或者从后向前考虑,可以 O(n)。
B. Difference Array
你有一个初始长度为 n 的有序数组 a(从小到大)。设 a 当前长度为 l,你要对 a 作差分,即令 bi=ai+1−ai(1≤i<l),然后将 b 数组从小到大排序,接着让 ai=bi(1≤i<l),并继续执行上述操作。
显然,每一次操作后 a 数组的长度都会减少 1;执行 n−1 次操作之后,a 中只会剩下一个元素,请你输出这个剩下的元素。
首先有个不难发现的是相同的数,只需要有一个贡献即可。
然后发现会有 O(√S) 个本质不同的数,也就是说,至多需要删 O(√S) 次,每次删一个的代价是 O(nlogn),于是你会发现这跑的飞快。
因为这复杂度是 O(Slogn) 而不是根号的。考虑利用构造证明,如果需要每一次都只减少 1 个值,而不产生相同的值,那么最优秀的显然是 f(x)=2x,因为存在 Δf(x)=f(x−1),于是这种东西至多是 O(logS) 个的,所以复杂度不会高于 O(nlogS),然而存在神秘证明,操作次数是 O(Sn) 的,所以复杂度是 O(Slogn) 的,这看题解即可。
C. DFS Trees
有一图有n个点m条边。第i条边的权值是i
现在有一个错误的最小生成树算法(就是有bug)
vis := an array of length n
s := a set of edges
function dfs(u):
vis[u] := true
iterate through each edge (u, v) in the order from smallest to largest edge weight
if vis[v] = false
add edge (u, v) into the set (s)
dfs(v)
function findMST(u):
reset all elements of (vis) to false
reset the edge set (s) to empty
dfs(u)
return the edge set (s)
每次调用findMST(i)时,会返回这个图的最小生成树,你要判定那些是正确的最小生成树。
首先,最小生成树是唯一的,于是整张图会变成一棵树加一些边。
在 DFS Tree 里面,只有返祖边,我们考虑每一天非树边在什么时候满足为反祖边。
如果 (x,y) 不为祖先关系,那么是各自子树内即可。树上差分即可。
D. Partial Virtual Trees
给定 n 个点的树以及质数 p,对每个 k=1,2,⋯,n−1 计算满足下述条件的点集序列 {Si}ki=0 数量 mod p:
- {1}=Sk⊊Sk−1⊊⋯⊊S0={1,2,⋯,n};
- 对每个 Si 以及 u,v∈Si,都有 LCA(u,v)∈Si。
其中 LCA(u,v) 表示以 1 为根时 u 与 v 的最近公共祖先。
真子集很恼火,于是利用容斥搞掉。
逆着删元素,于是 fx,i 表示 x 在 i 时被删除,初始全部点亮。
于是儿子必须全部消失或只剩下一个,fx,i 才能消失,转移参考 https://www.luogu.com.cn/blog/_post/454338 即可。
1685
A. Circular Local MiniMax
检查是否存在对于 a1,a2,⋯,an 的重新排列,使得 i 从 1 到 n 中至少有一个以下条件成立。
- ai−1<ai>ai+1
- ai−1>ai<ai+1
首先奇数长度一定不可以,于是只用考虑偶数。
此时只需要分成两个部分,交错放置,然后判断即可。
B. Linguistics
Alina 发现了一种只由 A, B, AB, BA 四种单词组成的语言,这种语言不含有空格,书写句子时只需要将单词直接连接起来即可。
她很好奇是否可以用 a 个 A,b 个 B,c 个 AB,d 个 BA 拼接形成句子 s。
换句话说,是否可以用 a+b+c+d 个单词以某种顺序拼接出句子 s,这 a+b+c+d 个单词都得被用到,他们的顺序可以由你决定。
很抽象的贪心。考虑先把形如 XYXYX... 的段都提出来,按照长度排个序。
首先如果长度为偶数,那么优先满足 XY 的。在满足了所有的偶数之后,我们继续考虑奇数的,此时发现,两者可以满足的数量相加是恒定的,所以可以任意满足。
最后剩下了一点偶数的,那么贪心的放即可。
C. Bring Balance
Alina 有一个长度为 2n 的括号序列 s,她想把这个括号序列变成一个合法的括号序列。每一次操作可以 reverse
s 的任意子串,求最小操作次数。
经典的画折线,发现至多需要两次,也就是翻转至高点左右两侧。
考虑什么时候只用翻转一次,找到至少翻转的那个区间 [l,r],找到左右两部分最高的点 L,R,如果 ∀i∈[l,r],hi≤hL+hR,那么可以只用一次,正确性显然。
D. Permutation Weight
给定一个 1∼n 的排列 p, n≤200
对于一个 1∼n 的排列 q,定义其权值为:
找出 任意一个 权值最小化的 1∼n 的排列 q。
Hard Version 是找到字典序最小的。
先考虑简单版的怎么做。
设 f=p−1,先不考虑 q 是否是一个排列,那么存在 qi=pqi+1,两边同时套上 f 得到 fqi=qi+1。
于是,可以钦定 q1=1,于是可以推出 q 的样子。
然而发现 q 存在多个环,推不出来,所以需要交换某些东西,使得只存在一个环。为了达到下界,我们只能交换相邻的 fi,fi+1,保证损失最小。
利用并查集维护可以做到 O(n)。
然而对于字典序最小,看到数据范围容易想到,每位钦定的选择某一个值,判断这个前缀是否可行,可以做到 O(n3)。
我们重新看简单版的过程,我们有很多 qi→fqi+1,考虑得到了一些 (i,fi),qi 则规定了其顺序,而答案则是 ∑(qi,fqi)[0]−(qi−1,fqi−1)[1]。
于是我们根据 fi 可以划分出一些环,发现在这些环内跳是没有代价的,这十分优秀。
现在转而考虑 fqi−1→qi 的这条边,发现需要经量使得两者接近,并且所有连完之后满足:
- 所有边只形成一个环。
- 使得所有 fi 划分出来的环联通。
而为了使得答案最优秀,我们需要:
- 每个边不会重复合并划分出来的环。
- fqi−1→qi 形成的图中,每个环编号都是连续的,且可以被拆分为递增和递减的两个段。
此时我们需要判断一个前缀是否合法/优秀,于是就可以采用如下方法:
- 不存在两条单增链相交,不存在两条单减链相交(自环同时算作单增链及单减链)。
- 不存在未连边的点同时被单增链及单减链覆盖。
- 不存在被覆盖的某边 (i,i+1) 在同一个划分的环内。
- 剩下的所有作为边界的 (i,i+1) 可以使得所有颜色联通。
这可以利用差分 + 并查集每次 O(n) 的维护。
于是总复杂度为 O(n3)。
GYM 103495
这里挑选部分值得一谈的题。
B. Among Us
给出一张带边权的无向图,边权可被视为长度。最开始有 2 个内鬼和至多 8 个船员,内鬼起始位置给出,内鬼只能沿着边走。内鬼知道一系列形如“船员 p 会在第 t 秒出现在顶点 x 的信息。若内鬼与船员在同一时间和同一地点出现,则 内鬼可以把船员给砍了。内鬼只能在前面给出的时间/地点砍人。你需要为 2 个内鬼分别规划路径使得它们能够尽快将所有船员都给砍了。若方案存在则输出最短时间,否则输出 −1。
核心:状压,最短路,DP。
首先利用状压将两个内鬼分开,于是问题简化为对于一个人。
不难想出一个状态,设 fx,t,S 表示是否能够在杀了 S 中的人后在 t 时到 x。
如果固定 x,S,发现对于一个后缀 t 都生效,那么可以将 t 作为值,于是状态为 fx,S 表示杀了 S 中的人并到达 x 的最短时间。
接下来转移有两种:
- →fy,S,也就是移动到 y。
- →fx,S|2k,也就是在 x 蹲守某个人。
类似 djk 的跑即可。
D. Pattern Lock
给定 n 行 m 列的点阵图,用一条折线经过所有的点各一次,要求每条线段不经过除端点外的其他点,且形成的角都是锐角。
对于 n,m 存在偶数的情况是简单的。
这样两行两行的构造即可。
对于都有奇数的,那么先构造一个 3×3 的,接下来会分出两个偶数矩阵即可。
大概就是
E. Stone Ocean
定义一个长为 n 串的权重 w(s) 为有多少个排列 p 使得 s[p1]s[p2]…s[pn] 是一个回文,现在有 n≤30 个串,从每个串随机选一个位置的字符,连接成一个长为 n 的新串,求这个新串的 w 的期望。
先考虑如何计算 w(s)。先判断是否能够成为回文,如果可以,枚举哪两个会配对,最后乘上 (n2)! 来修正这些配对的位置即可。
于是类似的,枚举哪两个字符串会配对,记 mat(i,j) 表示 si,sj 选择相同字符的方案数。那么总贡献即是 (n2)!2n2∏mati,j 其中 2n2 是选择一个子集作为左部,∏ 内的东西是所有配对的对。注意在奇数的情况下需要新建一个虚点 x 使得 mati,x=|si|,与虚点匹配的放在最中间,还要注意这个虚点不会影响前面的系数。
考虑 n 很小,进行状压,f[S]×mati,j→f[S|2i|2j],这是容易理解的。最后除以方案数即可得出期望。为了不重复贡献,每次 i 只需要设为最小的即可。
乍一看是 230 的状态数,然而实际上远远没有这么恐怖,由于我们每次选择了最小的 i,对于状态,必须满足前缀 1 的个数 ≥1 的个数的一半,于是状态数大概为:
在 n=30 时 =1346269(实际也是如此),利用类似队列的思路转移,那么只需要 ×30,计算级别在 4×107,能过。
题解状态数分析就是一坨,不好评价。
F. Jumping Monkey II
对每个点 x,以 x 为起点出发时,求所有简单路径的 LIS
,注意 x 必选。
首先容易得出一个 O(n3logn) 的解法,也就是 O(n2) 的枚举路径,然后 O(nlogn) 的求 LIS
。
其次,枚举每一个根 rt,设 fx,i 表示以 i 开头的 LIS
的最长长度,这容易利用线段树合并完成 O(n2logn)。
发现这是 O(n2) 树上状态问题,考虑分治。这里的话边分治更简单,并且常数很小。
只是注意这里边分治需要在三度化的时候将点权下放到边权上:
最终复杂度 O(nlog2n)。
G. Five Phases
给定一个 5 元环,需要进行 k≤105 次操作,每次操作可以进入如下操作之一,求到达目标局面的方案数。
- 单点 ±1 或者全局 ±1。
- 相邻的 3 个 ±1。
- 相邻的 2 个 ±1,对面的那个 ∓1。
联想到生成函数,是 5 元的:
经过通分化简,可以得到:
将 1xyzwv 提出去,得到:
先考虑一个简化的问题:[xaybzc]((1+xy)(1+yz)(1+zx))k。
考虑有哪些组合 (xy)l(yz)m(zx)n 可以贡献到目标,列出等式:
发现 l,m,n 都是唯一的,类似的推广,也就变成了 5 个组合数相乘即可。
H. Reverse the String
给定字符串 S,至多可以翻转一个区间,求最小字典序。
首先发现翻转的开头是一定的,也就是第一个存在 Sj<Si 的那个 i。
之后也就只有 O(n) 种可能,利用 hash
+ 二分比较字典序即可。
L. Tree Game
给一棵树,每个点有权值,权值是一个排列。从叶子开始,每次重新排列 x 和其所有儿子,然后向上递归。目标权值等于结点编号,给定其中一些权值,问有多少可能的权值排列可以排序成功。
首先考虑全部确定的情况,发现只需要不断交换模拟即可。
而加入 0 之后呢,发现判断还是只需要模拟即可,只是 0 是万金油,可以等于一切。
于是呢,考虑当前节点 x,现在可以重排一些东西,和一些 0,发现其实这些 0 任意排列都可以,也就是可以贡献一个 cnt!。而又无法影响到子树内部,所以可以得到递推式:
随便做即可。
GYM 103743
B.Prime Ring Plus
将 1∼n 分成若干个环,每个环上相邻两数之和是质数,n≤104。
O(n2) 的枚举可以放在一起的,发现奇偶形成二分图,那么跑一个网络流即可,注意一个需要连两个。
复杂度 O(n√n2)=O(n2)。
D. Finding Pairs
给定一个序列和一个数 k,每次询问一个区间,问在区间中找若干对不含重复元素的距离为 k 的数的最大权值和。
将 modk 相同的弄出来,可以作为一个单独的问题。
如果是暴力,自然想到设 fi,0/1 表示考虑了前 i 个数,最后一个数选没选的最大价值。
于是考虑可以莫队,但是回滚,设 fi,0/1,0/1 表示 modk 为 i 的序列的 DP 数组,那么稍微特判一个只有一个/两个数的情况转移即可。
复杂度 O(n√n)。
E.Playing Cards
给定序列 A,B,以及一个 k≤109,构造一个排列 P,使得下式最小:
有一个不是那么优秀的做法,每次取出最小的 Ai,如果存在比 Ai 小的 Bi,贪心匹配 即可,否则将 Ai←Ai+k,并 ans←ans+1,将 Ai 重新放入堆中,这样是 O(n2logn) 的。
注意这似乎不太好优化,我们将对于 Ai 的 +k 变为对于 Bi 的 −k,那么做法变为从大到小枚举 Ai,如果存在 Bi>Ai,那么不断 Bi←Bi−k,ans←ans+1,直到所有 Bi≤Ai,此时 Ai 与最大的 Bi 配对即可。
利用线段树上二分即可优化为 O(nlogn)。
F. Pockets
n 种物品价值 vi,体积 wi,无限个,总容量 k。
如果购买 i 个物品,则总容量变为 i+k。
至多购买 m 个物品,求所有合法购买方案的价值之和,定义一个方案的价值为 ∏vi。
对于多项式还是不会……考虑单次购买的生成函数:f(x)=∑vixwi。
于是答案为:∑mi=0∑i+kj=0[xj]fi(x)。
注意到后面是生成函数前缀和可以转化为:∑mi=0[xi+k]fi(x)11−x。
然而这个 [xi+k] 很难处理,考虑将系数平移一下:
然后可以将 fi(x) 转化为 fm−i(x) 的形式:
后者可以利用 1−xk+11−x=∑ki=0xi 的形式化简:
再稍微化简一下:
多项式全家桶即可。
H. Super Gray Pony
求长为 n 的 0/1 串 S 求 k 次格雷码后的结果,求格雷码是指将 S 看做二进制数,求其在 n 阶格雷码序列中的下标。
格雷码有个小小的性质:Rank(x)=x⊕(x>>1)。
于是求 k 次对于前面的 Sx+i 来说其贡献次数为 (ki)。
利用 Lucas
来分析,只有 i&k=i 的那些 Sx+i 会对于 Sx 做出贡献。于是对于每一个地方来一次子集卷积?
考虑 DP 的思路,设 fx,i 表示对于 x 只考虑 k 的前 i 位构成的子集的贡献。
初始 fx,0=Sx,考虑第 i 位,如果为 0,那么不会存在新的子集,那么 fx,i=fx,i−1,如果为 1,那么可以存在新的子集,其从 fx+2i,i−1 转移而来。
也就是 fx,i=fx,i−1⊕fx+2i−1,i−1。
交换一下枚举顺序,先枚举 i,那么可以利用 bitset
O(nlognw) 的完成。
J. Balanced Tree
求 n 个点组成的每个节点都满足左右子树大小相差至多 1 的二叉树个数。
容易想到一个记忆化搜索的方式:
不难证明状态数是 2logn 的,答案一定形如 2c,可以变为指数加法。
直接记忆化复杂度是 O(Tlognloglogn) 的,无法通过,但是可以通过分层记忆化的方式优化到 O(Tlogn),只是常数略大,需要狠狠卡常才能过。
此时其实会发现每一层至多两个状态,模拟这个过程即可去掉记忆化的常数。
然而 @The_Last_Candy 给出了一种不同的方法。
将对于指数加法的式子改写为:
发现偶数的地方会贡献一个 2,那么考虑是否可以通过类似数位 DP 的方式求得所有转移时为偶数的个数。
设 fi,0/1 表示考虑了前 i 位,当前位为 0/1 的个数,然而由于存在 −1,所以还需要增加一个 0/1 表示是否向前借位。
那么对于转移:
反着来变成 DP 减少常数,最后指数就是 ∑fi,0,0。
GYM 104396
B. Honkai in TAIKULA
给定一张 n 个节点 m 条边的有向带权图,对每个节点,若不存在奇权圈(可经过重复顶点、重复边,若经过重复边,边权计多次),输出 Battle with the crazy Honkai
,否则,输出最小奇权圈的权值(负无穷输出 Haha, stupid Honkai
)。
由于需要存在圈,那么圈一定在强连通分量内,那么可以利用 tarjan
分为若干强连通子图进行求解。
考虑由于需要奇偶分开,那么拆成奇偶两个点即可。
考虑如果是跑 SPFA
的话就需要 O(nm2) 的复杂度,显然不太能过,所以需要用到 Johnson
全源最短路算法,优化为 O(nmlogn)。
注意有负权环存在的情况。
D. Rail Star
二维平面上有 n 个点,设 Ai,j 表示将第 i 个点删除后,将剩下 n−1 个点划分为两个集合后,提取其中点数为 j 的集合,这个集合的种类数。求出该矩阵。
类似于 1860F 的想法,一共 O(n2) 种斜率,带来 O(n2) 种映射方式,每种映射带来的一种排序都会对于答案做出贡献。
这 O(n2logn) 很容易维护这一个排序方式,但是我还不太清楚该如何维护答案。
E. LCM Plus GCD
输入 x,k,求大小为 k 的不同的集合个数,其中所有数的 gcd+lcm=x。
考虑 lcm=kgcd,那么枚举 gcd 即可得到 k。
观察每一个数,将 k=∏pici,那么对于每一个数的分解,再除以 gcd,指数需要满足 min=0,max=ci。
考虑实际上因数的个数 ≤9,那么考虑状压容斥,先枚举有哪些没有满足下界,在这个基础上枚举有哪些没有满足下界,复杂度 O(Tm22m)。
G. Moving Boxes
n 个物品在一条直线上,每个需要从 xi 移动到 yi ,有一个机器人移动,机器人每次只能拿一个物品,速度为 1,转向需要 c 秒,最后需要回到起点,方向回到初始方向。求运输完所有物品需要的最小时间。
贪心,还不太会。
L. Architect
给出 n 个长方体,询问其是否拼接成一个 W×H×L 的立方体,没有重叠和空隙。
- 做法一,先判断体积和。将全部无交容斥为有交,数点问题即可。
- 做法二,利用随机化将每个立方体的体积放缩,如果满足要求,那么无论如何放缩体积都是合法的,多次随机充分 → 必要。
- 做法三,还是先判断体积和,从一维结论合理外推,充要条件是每个点都覆盖了偶数次,利用
map
判断即可。
↓2024.03.04
1936
A. Bitwise Operation Wizard
交互题,每次可以询问 ai|aj 与 ax|ay 的大小关系,求最大的 ai⊕aj,其中 ai 是一个 0∼n−1 的排列。
首先贪心的肯定要选择最大的那个,然后选择一个数(选了最大的,自然这个就是最小的了)凑出一个 2k−1。
那么根据 x|x=x,利用 O(n) 次形如 (i,i,j,j) 的询问即可找出最大。
找到了 i 最大,接下来需要 ai⊕aj=2k−1,只需要找到 ai|aj 最大的那些 j 中最小的那个即可。考虑满足 ai|aj 最大的一定是 ai 补集的超集,而需要恰好凑出全部,那么自然是最小的那个,分别 O(n) 次即可。
B. Pinball
给定一个 <>
组成的字符串,当一个球至于其上某一个位置,其会按照一下规则运动:
- 如果所在字符为
<
,那么向左一位,否则向右。消耗 1s 时间。 - 当球移动之后,原本所在位置字符取反,即
<
变成>
,反之同理。 - 如果移出串内,则停止运动,既可以是左边,也可以是右边。
多组数据,给定长度为 n 的串,分别求出把球放在 1∼n 这些位置需要多少秒才能走出这个串。可以证明一定可以在有限步内走出。
先考虑模拟,比较容易发现,实际上球在一段反复走,向两边拓,于是可以变成双指针 O(n2) 求。
观察双指针的本质,设出发点在 >
,分讨出发点左侧(包括)>
的个数 S0 和右边 <
的个数 S1。
当 S0>S1,那么最终会从字符串右边出去,推推式子即可;当 S0≤S1 则反之,也是推推式子即可。
C. Pokémon Arena
有 n 神奇宝贝。一开始,竞技场上只有第 1 只神奇宝贝。
每只神奇宝贝都有 m 种属性。而第 i 只神奇宝贝的 j 属性是 ai,j 。第 i 只神奇宝贝的雇佣费用是 ci 。
为了最终让 n 只神奇宝贝站在竞技场,可以按照任意顺序执行以下两种操作:
- 花费 k(k>0)的代价,将 ai,j 永久增加 k 。
- 花费 ci 代价雇佣第 i 只神奇宝贝与当前场上的神奇宝贝 k 决斗,并选择任意属性 j。如果 ai,j≥ak,j ,则 i 击败 k,i 上场,否则保持不变。
求出让第 n 只神奇宝贝站在竞技场上所需要支付的最低费用。
傻逼翻译。
最直观的想法是,一个神奇宝贝一定不要上场两次,否则直接跳过中间部分。
另一个十分重要的观察:不会连续选择相同的 j。否则不如直接跳过中间次决斗,省下雇佣费。
相当于只有去决斗前才会调整 ai,j,并且之后都不会在用到这个值。
于是如果让 i 用属性 j 与 k 决斗,那么一定需要的花费是 ci+max(0,ak,j−ai,j)。
利用这个东西建图,可以做到 O(n2m)。考虑可以优化建图。
将 ci 提出来,拆点处理,接下来考虑 max(0,ak,j−ai,j) 的边,发现实际上只需要在同层间前后缀一下即可。
于是建出图来跑 DJK 即可。
D. Bitwise Paradox
我原本以为只是支配对相关的题目,因为只有 O(n) 个对,但是带修就炸了。考虑 bi|bi+1|…|bj 有一个很好的性质,在固定 i 的情况下,只有 O(logV) 种取值。
所以放在线段树上,维护前缀,后缀,与值与最大值,合并即可。
直接合并是 O(log2V) 的,但是利用支配对考虑,双指针合并变成 O(logV)。
总复杂度 O((n+q)lognlogV)。
E. Yet Yet Another Permutation Problem
首先有一个很 naive
的 O(n2) DP:
前面直接做,后面前缀和即可,最后答案是 fn,n。
没什么优化的前途,考虑 P 的前缀 max 数组 Q。
考虑构造出来的排列和 Q 存在某一位相同,设为 i,那么需要满足 Qi 在 i 或者之前出现,并且 i 前都没有比 Qi 大的。
逆着不好考虑。顺着不会考虑,还是没太懂题解的 DP 是怎么来的。
TODO。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?