2025暑假集训做题记录
luogu P7118 Galgame
前置
- 有 \(n\) 个点的二叉树形态总数是 \(Catalan(n)\)。递推公式:$$\frac{Cat(n)=Cat(n-1)\times(4n-2)}{n+1}$$。
solution
没有给定 gal 有趣的 gal 可以分成两类:节点个数比它少 和 其它。
节点个数比它少的可以直接用 \(Cat(siz_u)\) 计算。接下来考虑剩下的情况。
对于递归进去处理的一个节点 \(u\):
-
若当前左子树的 \(siz\) 比给定小,一定满足条件。即 \(\sum _{i=0}^{siz_{lson_u}-1} Cat(i)\times Cat(siz_u-1-i)\)。
-
左子树的 \(siz\) 与给定一样,但有趣度更少。此时右子树的形态是随意的。所以递进去处理,同时带上 \(Cat(siz_{rson})\) 的系数。
-
右子树的 \(siz\) 与给定一样,但有趣度更少。因为左子树的形态必须与给定一致所以没有系数。
这样的复杂度是 \(O(n^2)\) 的。瓶颈在于第一种情况,当 \(siz_{lson}\) 大的时候非常不优。
但我们发现 \(Cat(siz_u)=\sum _{i=0}^{siz_{lson_u}} Cat(i)\times Cat(siz_u-1-i)+\sum _{i=siz_{lson_u}}^{siz_u-1} Cat(i)\times Cat(siz_u-1-i)\),而前后的枚举个数和是 \(siz_u\),所以当第一项大时可以用总的减去第二项。此时复杂度显然是 \(\log\) 的。
于是做到 \(O(n\log n)\)。
某模拟赛神秘题
http://goj.wiki/d/Union2024/p/853?tid=686bd4da0e50d6389dc89526
给你一个 \(n\) 个点的“堆”,但每个节点不一定只有两个子节点。对每个点进行编号,编号满足堆的条件。给你一个排列 \(q_i\),求给每个位置赋值使得值的大小满足堆的条件,且字典序小于等于 \(q\) 的情况下字典序最大的排列 \(p\),并求满足字典序小于等于 \(q\) 且满足堆条件的排列方案数。
1
考虑贪心。按照编号顺序一个个填。
已经填完的数一定形如包含根的一个连通块。扣去当前要填的数,剩下要填的数类似组成几个子树。
几个子树各有上面传下来的一些限制。考虑当前限制最大是多少。
显然,给一棵树一些数,一定有合法的填法。所以在考虑子树时只考虑给每个子树分哪些数填。\(^1\)
对于每个子树的限制排序,从小到大填。如果当前填到了一个 \(a\),但是下一个子树的限制是 \(a+?\),那么显然 \(a\) 只能填到当前的位置上。填的时候如果不满足字典序就和 \(q_u\) 取 \(\min\)。
为什么子树 \(q\) 的限制一定能满足?若不满足,则说明填的数 \(>q_u\),那把 \(q_u\) 直接换进当前位置即可。只是改了分配的数所以还是有合法的填法。
2
设 \(f_u\) 为以 \(u\) 为根,有 \(siz_u\) 个数,有多少种填数方法。
枚举第一位小于 \(q\) 的位置 \(i\)。前面的数完全按照最大的去填。剩下子树的 limit 是很多个 \(\ge ?\) 和特殊的 \(i\) (\(\ge?\) 且 \(<q_i\))。发现 \(i\) 的限制可以转换为全都是 \(\ge ?\) 的方案数减去 \(\ge q_i\) 的方案数。按照限制从大到小计算,式子是容易的,不进行展开。
没了啊?。
咕咕咕。
CF538G
?
P7320
考虑叶子,如果有点集直接输出。边集在叶子间连边即可。
AT_arc103_d
\(D_u\) 最小的一定是叶子,通过等式找到这个叶子一定会和什么点连边,递下去找。
CF611H
转化为多个点集?大概是枚举一条边删掉之后是否合法然后暴力做。
CF131E
归纳到 \(n-6\),小情况暴力。
upd:但是到要暴力到12内。(貌似)更优越的做法:直接选那些点染成什么色。
CF1270E
发现不交非常难刻画,所以取模。发现欧几里得距离是平方,而平方对 4 取模有良好的性质。对原网格黑白染色,如果有颜色不同的点就直接输出(同色的点之间距离模 4 一定是 0/2,反之是 1),否则将网格旋转 45度 继续做。
AT_agc027_d
把网格黑白染色然后转化成黑mod白。如果白固定了,黑直接取 \(lcm(a,b,c,d)+1\) 即可。发现这样无法保证黑格的数互不相同,考虑白格全部取质数。但这样太大了无法接受。神秘构造方法:对于每一条对角线的白格(正着反着都算)乘一个素数。容易发现这样符合条件。
CF804E
只有在 \(n \mod 4 =0 or 1\)时有解。归纳至 \(n-4\)。
大概酱:
CF1690G Count the Trains
唉唉还是没有蓝色思维。
\(O(n)\) 维护出一开始的答案是容易的。
考虑对于一个修改 \(x,y\),对答案有什么影响:
- 在 \(x\) 后面的任意车头 \(v \ge a_x-y\) 那么会被合并到当前这节火车(并不需要知道车头是多少)。
- 在 \(x\) 前面的一节车头如果 \(v>a_x-y\) 那么这节火车就会在 \(x\) 处断开,\(x\) 成为新的一节火车车头。
用 set 维护两个操作。于是做到 \(O(n \log n)\)。
CF1638E Colorful Operations
考虑维护每个颜色的总 tag,线段树维护每个区间的区间加标记,颜色覆盖标记。每次修改颜色的时候递归到区间颜色只有一种再操作,区间覆盖颜色,区间加上原来颜色的 tag,减去修改后颜色的tag(这个tag之前操作的时候并不影响到当前区间,但是后面计算当前区间的时候会算上,所以减掉贡献。)。查询由于是单点直接做就好了。
考虑这么暴力为什么复杂度是对的。感性理解就是,一个颜色区间只会在被覆盖时贡献 \(O(1)\) 的复杂度。又知道一个颜色区间并不能被贡献很多次(如果可以的话那每次修改的区间也仅仅是 \(O(1)\) 的,不然就会完全覆盖掉一个区间。)。
这个均摊非常的妙啊。原理应该跟珂朵莉树类似,但是不想学,咕咕咕。
AT_agc061_c [AGC061C] First Come First Serve
随便选是 \(2^n\) 的。考虑何时会算重。
当 \([a_i,b_i]\) 中没有人时,第 \(i\) 个人的选择就是不影响顺序的,就算重了。
所以转化成算每个人的选择满足下列条件的方案数:
- 选 \(a_i\)
- \([a_i,b_i]\) 中有至少一个人,选 \(b_i\)
考虑容斥变成:
- 选 \(a_i\) 或 \(b_i\)
- 减去 \([a_i,b_i]\) 没有人的选 \(b_i\) 的情况。
设 \(dp_i\) 表示前 \(i\) 个人考虑容斥系数的方案总数。第一种转移是显然的。考虑第二种转移,对每个 \(i\) 找出跟它无交的最后一个位置 \(l_i\),跟它有交的最后一个位置 \(r_i\)。若 \([a_i,b_i]\) 为空,那么 \([l_i,r_i]\) 的选法就是固定的(因为这中间的位置有一个端点在 \([a_i,b_i]\) 内。)于是 \(dp_{r_i} \gets -dp_{l_i}\)。
\(l_i,r_i\) 求出是简单的。复杂度 \(O(n)\)。
U486901 传送
线段树分治好题,傻逼评测机波动。
description
给你一个无向图,第 \(i\) 条边 \((a,b)\) 会在 \([l,r]\) 时刻出现。假设所有能从 \(1\) 到达 \(i\) 的 \(k\) 时间点为 \(t_{i, 1},\cdots,t_{i,k}\),,求 \(\operatorname{xor}_{i = 1} ^ n (\sum_{j = 1} ^ k t_{i,j})\)。
solution
一看就是线段树分治。考虑暴力做法,每次分治到 \((i,i)\) 时把和 \(1\) 一个连通块的点答案全部 \(+i\)。这样显然是 \(O(n^2\log n)\) 的,无法通过。
但是我们并不需要时刻知道每个点的答案,所以考虑对于每个点维护一个 \(tag\),代表每个点给它的子树的贡献。后面的想法比较自然,把 \(i\) 并入 \(j\) 时,\(tag_i\gets tag_i-tag_j\),因为 \(j\) 之前的 \(tag\) 并不需要加到 \(i\) 上,但最后会计算到。把 \(i\) 分离 \(j\) 时,\(tag_i\gets tag_i+tag_j\) 即可。最后每个点的答案就是 \(tag_u\)。
时间复杂度 \(O(n\log^2 n)\)(吗)。
corner case 1
为什么在把 \(i\) 分离 \(j\) 时不用算更高的祖先的贡献?
考虑可撤销并查集的性质,分离的时候一定和合并的时候形态一样。那么 \(j\) 一定是根。
submission
卡了很多发。link。
2025.7.25模拟赛 D交集
改出这题需要感谢:lzy(优秀的讲题),ymy(调出关键一步),lhl(提供多个可用hack),chx,yjc 以及 ljq(提供对拍代码)。
题意
数轴上有 \(n\) 条线段。定义一组线段的价值为该组所有线段的交集长度。并且要求每一组的价值都至少为 \(1\),现要求把这些线段恰好分为 \(k\) 组是的这 \(k\) 组的价值总和最大,输出这个值。无解输出 \(0\)。
solution
线段之间没有完全包含关系的时候是简单的。设 \(dp_{i,j}\) 表示到第 \(i\) 条线段,分了 \(j\) 组的最大价值和。直接单调队列 dp 即可,由于自己写出来了就不展开了(虽然还是错完了)。这里复杂度 \(O(nk)\)。
如果有包含关系,设所有 完整包含了其它至少一条线段 的线段为“大线段”。那么如果“大线段”要和其它任何线段分到同一组,和它包含的线段(如有多条任意一条)放一组是不劣的,因为这样并不会影响答案贡献。否则就自己一组。
枚举有几条“大线段”自己一组,直接取它们之中最长的就行。
写出来大概就是:
其中 \(sum_i\) 表示前 \(i\) 大的“大线段”和。
复杂度 \(O(nk)\)。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
const int maxn=5004,inf=1e9+1121;
int n,m;
pii o[maxn];
struct node{int l,r;}a[maxn];int na;
int len[maxn],nb;
int dp[maxn][maxn];
deque <int> p[maxn];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>o[i].fi>>o[i].se;
sort(o+1,o+1+n);
for(int i=1;i<=n;i++){
bool fl=0;
if(o[i]==o[i+1]){len[++nb]={o[i].se-o[i].fi+1};continue;}//对于完全相同的线段只保留一条
for(int j=i+1;j<=n;j++){
if(o[i].se>=o[j].se){fl=1;len[++nb]={o[i].se-o[i].fi+1};break;}
}if(fl) continue;
for(int j=i-1;j>=1&&o[j].fi==o[i].fi;j--) if(o[j].se<o[i].se){fl=1;len[++nb]={o[i].se-o[i].fi+1};break;}//判“大线段”
if(!fl) a[++na]={o[i].fi,o[i].se};
}sort(len+1,len+1+nb,greater<int>());//取最大的“大线段”
dp[0][0]=0;p[0].push_back(0);
for(int i=1,j;i<=na;i++){
dp[i][0]=-inf;
for(int k=min(i,m);k>=1;k--){
while(p[k-1].size()&&a[p[k-1].front()+1].r<a[i].l) p[k-1].pop_front();
if(p[k-1].size()){
j=p[k-1].front();
dp[i][k]=dp[j][k-1]+a[j+1].r-a[i].l+1;
while(p[k].size()&&dp[p[k].back()][k]+a[p[k].back()+1].r<=dp[i][k]+a[i+1].r) p[k].pop_back();
p[k].push_back(i);
}
else dp[i][k]=-inf;
}
}//dp
int ans=0;int sum=0;
for(int i=0,lim=min(nb,m);i<=lim;i++){sum+=len[i]; ans=max(ans,dp[na][m-i]+sum);}
cout<<ans<<'\n';
return 0;
}
7.26模拟赛T2 P1537 硬币机
题意
你有 \(n\) 个物品,可以按顺序得到 \(a_i,b_i,a_i\) 的价值。问对于每个 \(i(1\le i \le m)\) ,取 \(i\) 次可以取到的最大价值。
\(n\le 10^5,m\le 10^6\)。
场上想到
转化成一个体积为 \(1\) 价值为 \(a_i\) 的物品和一个体积为 \(2\) 和价值为 \(a_i+b_i\) 的物品,容易发现这样可以覆盖所有情况。
把所有物品按照“性价比”排序。
然后开始乱贪,显然假完了。
solution
做法来自 ymy,听起来是对的。
把体积为 \(2\) 的物品和体积为 \(1\) 的物品分开。设总共取了体积为 \(i\) 的物品,有 \(j\) 个是体积为 \(2\) 的物品。发现随着 \(i\) 的增大 \(j\) 不会减小,拿指针维护 \(j\) 即可。
upd on 7.29
呃呃呃昨天突然感觉我的做法没有甲烷。逻辑链十分严密啊。
最后 ymy 大师给拍出来了。没有特判 \(W>3n\) 的情况,判完就对完了。
我吃柠檬。
补一下贪心细节:如果直接按顺序拿能正好放完所有体积肯定是对的,剩下的情况只有“下一个物品体积为 \(2\),但剩余体积为 \(1\)”这种。那么显然我们有两种选择:
- 放掉当前物品,找下一个体积为 \(1\) 的物品。
- 放掉上一个体积为 \(1\) 的物品,取当前物品。
于是预处理出对于每个 \(W\) 按顺序取可以取到哪里,上一个 \(1\) 的位置,下一个 \(1\) 的位置。按思路模拟即可。
7.28
P1397 [NOI2013] 矩阵游戏
solution
容易发现题目中的转移矩阵形式都是:
(因为太不会 latex 了,所以下面摘一段题解。)
当 \(a\ne 1\) 时,有
由费马小定理
所以 \(\begin{bmatrix}a & b\\ 0 & 1\end{bmatrix}^{P-1}\) 在做 \(\bmod P\) 运算时就相当于单位矩阵 \(I\) !
\(a=1\) 的情况特判即可。
P4301 [CQOI2013] 新Nim游戏
solution
显然先手一定有必胜策略。
考虑第一回合后手的操作,如果能够将剩下数的异或和操作成 \(0\) 那后手就必胜了。所以先手剩下的数里面一定不能有异或和为 \(0\) 的子集。
发现线性基里的数就具有这样美好的性质。但如何保证取的总数最小?
我们要求剩下的数的和最大,即线性基里的和最大。直接把 \(a_i\) 排序,从大到小插入。容易发现这样构造的线性基一定是和最大的。于是做完。
CF895C Square Subsets
solution
先把每个数里面的平方数除掉,这样不影响答案。这样每个质因数在一个数里出现的个数是 \(0\) 或 \(1\)。
最终是平方数等价于出现次数和是偶数,等价于出现次数异或和为 \(0\)。这样就非常像线性基了。
考虑对于线性基外的数的任意一个子集,它们异或起来的和一定和线性基内的一个唯一子集对应。所以线性基外的数随便取都是一种答案。设线性基的大小为 \(k\),则答案为 \(2^{n-k}-1\)(不能不选)。
7.29 咋提选讲
GDCPC2024F
题意
给你一个无向图,设 \(k=\lceil \frac{m}{n-1} \rceil\),问是否存在点对 \((u,v)\) 使得它们之间存在 \(k\) 条边不相交的路径。
\(n\le10^5,m\le2\times10^5\)
solution
\(k\) 相当于至多 \(n-1\) 条边一组至少分出的组数。看到 \(n-1\) 想到树,于是直接维护 \(k-1\) 棵生成树,每次从前往后插入一条边。若插入不了这条边说明已经有解了。这样枚举在 \(m>>n\) 时显然不行。
但是这玩意有单调性啊!于是做完。
upd
细节巨多啊,复杂度大概是 \(O(log^2)\) 的。
AGC016E
solution
先考虑如何判断一只火鸡能不能留到最后。考虑倒退,如果一个人吃的火鸡是 \((x,i)\) 就说明 \(x\) 在此之前必须保留。对于每个 \(i\),处理出 \(S_i\) 表示有保留要求的 \(x\) 的集合。
经过一些分讨(写起来太麻烦了喵),发现 \(S_i \cup S_j\) 是可以保留 \(i,j\) 的充要条件。直接枚举即可。
P9731 [CEOI 2023] Balance
先考虑对于 \(S=2\) 怎么做。相差 \(\le1\) 的限制不好处理,考虑对于每个出现次数为奇数的题目添加一组为 \((x,0)\) 的评测,问题转化为两次评测数量相等。
若把先评测的向后评测连边,就变成找一个欧拉回路。因为度数都是偶数所以一定有解。
后面分治处理,咕咕咕。
upd
对于每一层,先通过找欧拉回路将其分成每个颜色出现次数之差不超过 \(1\) 的前后两个部分,分治进去处理就可以了。
图的构造方法是把颜色和层数分别对应连边。
补一手欧拉回路解法:在有解的情况下,dfs是有正确性的。每次直接遍历复杂度肯定假了(嘿嘿我不知道调了特别久然后又是 ymy 调出来的),但是你发现一条边不会用两次,所以当前弧优化,做完!
复杂度 \(O(ns\log s)\) 吧!
7.30【DIV1 & DIV2】模拟赛
T1 exam
description
给你 \(n\) 种题目,每种题目有 \(m\) 道。每种题目有固定的两个参数 \(a_i\) 和 \(b_i\)。任意两道题目(包括合并过的)可以合并,合并后 \(a'=a_i+a_j,b'=b_i+b_j\)。每道题目的“爽度”定义为 \(\frac{b_i}{a_i}\)。
给定两个整数序列 \(p_0=1,p_1,\cdots,p_k=100\) 和 \(v1,\cdots,v_k\)。如果一道题目的爽度 \(\frac{b_i}{a_i} \in (\frac{p_{i-1}}{100},\frac{p_i}{100}]\),那么它的“开心度”为 \(a v_i\)。
求 \(\sum a \le m\) 的最大开心度。保证 \(p\) 和 \(v\) 单调递增。
\(n,m\le 8\times10^3\)。
solution
我要锐评,本题难度主要在于读题和看到 \(v\) 单增。
因为对于每种题目都有 \(\sum a=m\),所以可以理解成取的数量没有限制。
做一次完全背包,对每个 \(i\) 预处理出 \(f_i\) 表示 \(\sum a=i\) 时最大的 \(\sum b\)。
新的“物品”就变成体积为 \(i\),价值为 \(f_i\),再做一次完全背包即可。复杂度 \(O(nm)\)。
T2 team
description
给你 \(n\) 个人,每个人有三个能力值 \(a_i,b_i,c_i\)。你需要选出三个人满足他们分别是三人中 \(a,b,c\) 最大的人。求 \(a,b,c\) 最大值的和的最大值。
\(n\le 1.5\times10^5\)。
solution
如果没有三个人分别最大的限制直接分别取最大的求和即可。先把人分别按 \(a,b,c\) 降序排序,设当前选的人分别是 \(i,j,k\)(初始均为 \(1\)),不合法的情况当且仅当 \(a_i\le a_j\) 或 \(a_i\le a_k\) ,对于 \(b_j\) 和 \(c_k\) 同理。那考虑如果将 \(i\) 往后移,因为 \(a_i\) 排序后是单调不增的,所以还是解决不了矛盾。所以只能把 \(j\)(\(k\))往后移。模拟即可。
赛场脑抽了用 set,直接用数组就行了。记录详情 - Genisis Online Judge。
T3 memory
description
你需要维护 \(n\) 个可重集合 \(S_1, S_2, \ldots, S_n\),初始均为空集合。共有 \(m\) 次操作,操作分为三种类型:
- 操作1:给定区间 \([l, r]\) 和数值 \(k\),对所有 \(i \in [l, r]\),在集合 \(S_i\) 中插入元素 \(k\)。
- 操作2:给定区间 \([l, r]\),首先计算这些集合的并集中的最大元素,若并集为空则跳过此操作。否则,对于所有 \(i \in [l, r]\),若 \(T \in S_i\),则从 \(S_i\) 中删除恰好一个 \(T\)。
- 操作3:给定区间 \([l, r]\),计算并输出这些集合的并集中的最大元素:若并集为空,则输出 \(-1\)。
\(n,m\le 2\times 10^5\)。
solution
几乎是暴力维护题。
感觉这个形式很像前几天做的那个颜色段均摊。所以建一棵线段树,每个节点用 multiset 维护区间插入的元素,和最大值(不要求是区间插入)。
- 操作1:直接暴力插
- 操作3:暴力查
- 操作2:先用操作3查出最大值 \(ans\)。对于一个区间,如果它被完全覆盖并且区间插入的数中有 \(ans\),删掉即可。否则,若区间插入的数里有 \(ans\),把 \(ans\) 插入到左右儿子区间中。递归查询即可。
复杂度我也不知道多少,反正是对的!