CSP2025 游记
游记
为什么停课还要上语文英语。
为什么我语文英语这么差。
2025.10.28
模拟赛。
T1 使,T2 使,T3 使,T4 使。
T1 打表,T2 卡 set,T3 不卡暴力,T4 不会。
你数据 252 个点,\(n,c \le 1000\),卡不掉 \(O(nc^2)\)?
2025.10.29
???
模拟赛。
15+0+35+0=50,no。
T1 是橙题改,T2 是蓝题,T4 是邪恶数据结构。
怎么一题不会。
题解
2025.10.27
AT_arc149_d
维护 \(<0\),\(>0\) 两段。那么一次 \(D\) 操作会有:
- 对于 \(<0\) 的段。
- \(a_i <0-d\),仍然 \(<0\)。
- \(a_i=0-d\),在这个时间结束。
- \(a_i > 0-d\),变为 \(>0\)。
- 对于 \(<0\) 的段。
- \(a_i <d\),变为 \(<0\)。
- \(a_i=d\),在这个时间结束。
- \(a_i > d\),仍然 \(>0\)。
发现每次就是把两段分裂成 \(O(1)\) 段,然后重新合并或者删除。
平衡树维护。时间复杂度 \(O(n\log^2 n)\)。
这里的合并需要使用有交合并,且并查集优化。
AT_arc112_e
简单观察发现,没被选择的点在 \(a\) 序列中对应一个区间。如果这个区间为 \([l,r]\),那么 \(a_{1\dots l-1}\) 和 \(a_{r+1\dots n}\) 的操作顺序一定。
同时,如果 \(x\) 这个值先操作一次,然后又操作一次,那么之前的那次操作显然被覆盖掉了。也就是我们只在意每个数最后的操作。
定义状态函数 \(f_{i,j}\) 表示后面 \(i\) 次操作中,有 \(j\) 次操作是某个数的最后一次操作的方案数。则 \(f_{i,j}=2\times j\times f_{i-1,j}+f_{i-1,j-1}\)。
那么答案就是 \(\sum\limits_{l=0}^{n}\sum\limits_{r=0}^{n-l}C_{l+r}^l\times f_{m,l+r}\)。时间复杂度 \(O(n^2)\)。
AT_agc074_b
首先如果 \(A\) 能变成 \(B\),那么 \(n\) 次操作一定可以。
具体地,维护出若干个区间,满足每个区间 \([l,r]\) 中 \(a,b\) 都恰好有一个 \(1\),且它们的位置不同。那么选择两个区间交换(交换的长度为它们的最小值),一定能使其中一个匹配。例如:
100 0001
001 1000
可以交换得到:
001 0100
001 1000
那不超过 \(\frac{n}{2}\) 次交换怎么做?简单观察发现,实际上上面的操作次数是 \(O(cnt_1)\) 的。那么由于 \(cnt_0+cnt_1=n\),所以 \(\min(cnt_0,cnt_1) \le \frac{n}{2}\)。在 \(cnt_1>cnt_0\) 的时候 \(01\) 反转一下即可。
对于无解的判断,就是 \(a,b\) 中 \(1\) 的数量不同。或者最后剩下的都是形如:
100..0
000..1
或
000..1
100..0
的区间。
P14316
区间数颜色问题。
考虑扫描线,扫 \(r\) 维护 \(pre_i\) 表示 \(\lfloor\frac{a_x}{a_y}\rfloor=i\) 时 \(\min(x,y)\) 的最大值。显然 \(i\) 只有 \(O(V)\) 种。加进来一个 \(a_r\)。
-
\(r=y\)。
根号分治。
- \(a_r < B\)。在加进来 \(a_x\) 的时候枚举 \(a_r=y\),那么就可以知道 \(\lfloor\frac{a_x}{y}\rfloor=i\)。那么在 \(x\) 后面第一个 \(a_r=y\) 时遍历所有的 \(i\) 即可,这样一个 \(x\) 只会对应一个 \(y\)。修改时间复杂度 \(O(n B)\)。查询时间复杂度 \(O(1)\)。
- \(a_r \ge B\)。枚举 \(\lfloor \frac{x}{a_r}\rfloor=i\)。那么对于每个 \(i\),\(x\) 是一段区间 \([l,r]\)。需要维护区间 \(\max\)。注意到对于同一个 \(r\),\(\bigcup\limits_{}^{}[l_i,r_i]=[1,V]\)。则维护块内前缀,后缀,整体 \(\max\) 后总复杂度 \(O(\sqrt{V})\)。查询时间复杂度 \(O(n\frac{V}{B}+n\sqrt{V})\),修改时间复杂度 \(O(n\sqrt{V})\)。
平衡后可以做到 \(O(n\sqrt{V})\)。
-
\(r=x\)。
有 \(|\{\lfloor\frac{w}{i}\rfloor| 1 \le i \le w\}|\) 是 \(O(\sqrt{w})\) 级别的。那么对 \(\lfloor \frac{a_r}{y} \rfloor=i\) 整除分块。需要维护区间 \(\max\)。同上。时间复杂度 \(O(n\sqrt{V})\)。
那么 \(pre_i\) 一共会有 \(O(n\sqrt{V})\) 次修改,查询次数 \(O(n)\)。维护 \(O(1)-O(\sqrt{V})\) 的区间加分块即可。
时间复杂度 \(O(n\sqrt{V})\)。
注意到 \(1.1\) 的那个暴力枚举的空间复杂度是 \(O(n\sqrt{V})\) 的,被卡了。考虑暴力枚举 \(a_x\)。因为如果 \(x < lst_r\) 时,\(x\) 对 \(lst_r\) 和 \(r\) 的贡献相同。所以没用。那么每个 \(x\) 还是只会对 \(O(B)\) 个数每个数贡献一次。空间复杂度变为 \(O(n)\)。
可恶,卡场。发现正解和这个差不多,但是对于 \(2\) 情况,数量实际上是 \(2\sqrt{V}\) 级别的,开 4s 和一位。
AT_arc128_d
删的数是若干个区间。
那么定义状态函数 \(f_{i}\) 表示最后一个数为 \(i\) 的方案数。则:\(f_i=\sum f_j [c(j+1,i-1)]\)。
那么问题就是一个区间能否删掉。
进一步发现,依赖 \(j\) 的数和依赖 \(i\) 的数形成了区间的前后缀。也就是说,如果区间能删完,一定存在 \(A_k \ne A_{j} \ne A_i\),使得 \([j,k]\) 中相邻两个数不同,\([k,i]\) 中相邻两个数不同。
记 \(pre_i\) 为 \(i\) 最远能到的位置 \(j\),使得 \([j,i]\) 中相邻两个数不同。那么只要 \(pre_i \le j\) 且 \([j+1,i-1]\) 中存在一个 \(k\),有 \(A_{k}\ne A_j \ne A_i\),就可以转移。
注意到如果 \([j+1,i-1]\) 不存在 \(k\),那么 \([j,i]\) 的颜色数量 \(\le 2\)。维护 \(lst_i\) 为 \(i\) 能到最远的位置 \(j\),是的 \([j,i]\) 中颜色数量 \(\le 2\)。
则:\(f_i =\sum\limits_{j=pre_i}^{lst_i-1}f_j\)。前缀和优化即可。
对于 \([lst_i,i-2]\) 的 \(j\),序列一定长成:\(0101010101...01010\)。发现这个时候只有 \(j=i-2\) 时可能能够转移。条件是 \(i-1\) 为另一种颜色。
对于 \(j=i-1\),显然可以转移。
时间复杂度 \(O(n)\)。
AT_arc127_d
如果求 \(\sum\limits_{i=1}^{n}\min(x \oplus A_i,y \oplus B_i)\) 怎么做。
枚举 \((x \oplus A_i)\) 和 \((y \oplus B_i)\) 的 \(LCP\)。那么如果 \((x\oplus A_i)\) 和 \((y\oplus B_i)\) 的前 \(j\) 项相同,则有 \(x,y\) 的第 \(1\sim j\) 项是否相同的限制。若相同则为 \(0\),否则为 \(1\)。
那么记 \(f_{s,0/1,0/1}\) 表示当限制为 \(s\) 且第一位没有限制的位 \(A_{i,j}=0/1,B_{i,j}=0/1\) 时的贡献。那么枚举 \(i\),维护 \(f_{s,0/1,0/1}\) 的时间复杂度 \(O(n\log V)\)。查询的时间复杂度 \(O(\log V)\)。
现在转到 \(\min(A_{i} \oplus A_j,B_i \oplus B_j)\),一样处理即可。时间复杂度 \(O(n\log V)\)。
拥有使代码:
il void solve(){
n=rd; int idx=0;
for(re int i=1;i<=n;++i) a[i]=(rd<<1)+1;
for(re int i=1;i<=n;++i) b[i]=(rd<<1);
for(re int i=1;i<=n;++i){
int s=0;
for(re int j=18;j>=0;--j){
int x=((a[i]>>j)&1);
int y=((b[i]>>j)&1);
if(!id[j][s]) id[j][s]=++idx;
if(x==y){
if(x==0){
f[id[j][s]][1][0][1].push_back(b[i]);
f[id[j][s]][0][1][0].push_back(a[i]);
}
else{
f[id[j][s]][0][1][1].push_back(b[i]);
f[id[j][s]][1][0][0].push_back(a[i]);
}
s=(s<<1);
}
else if(x==1){
f[id[j][s]][1][1][0].push_back(a[i]);
f[id[j][s]][0][0][1].push_back(b[i]);
s=(s<<1)+1;
}
else{
f[id[j][s]][1][1][1].push_back(b[i]);
f[id[j][s]][0][0][0].push_back(a[i]);
s=(s<<1)+1;
}
}
}
int res=0;
for(re int i=1;i<=n;++i){
int s=0;a[i]-=1;
for(re int j=18;j>=0;--j){
int x=((a[i]>>j)&1);
int y=((b[i]>>j)&1);
if(!id[j][s]) id[j][s]=++idx;
g[id[j][s]][x][y][0].push_back(a[i]);
g[id[j][s]][x][y][1].push_back(b[i]);
if(x==y) s=(s<<1);
else s=(s<<1)+1;
}
}
for(re int Id=1;Id<=idx;++Id)
for(re int i=0;i<=1;++i)
for(re int j=0;j<=1;++j)
for(re int k=0;k<=1;++k){
if(!f[Id][i][j][k].size()) continue;
if(!g[Id][i][j][k].size()) continue;
for(re int I=0;I<=1;++I)
for(re int J=0;J<=18;++J) cnt[I][J]=0;
int n1=(int)(f[Id][i][j][k].size());
int n2=(int)(g[Id][i][j][k].size());
for(auto x:f[Id][i][j][k]){
for(re int J=0;J<=18;++J){
int xx=((x>>J)&1);
if(xx) ++cnt[0][J];
}
}
for(auto x:g[Id][i][j][k]){
for(re int J=0;J<=18;++J){
int xx=((x>>J)&1);
if(xx) ++cnt[1][J];
}
}
for(re int J=1;J<=18;++J){
int val=(1ll<<J-1);
int c=cnt[0][J]*(n2-cnt[1][J])+(n1-cnt[0][J])*cnt[1][J];
res=res+val*c;
}
} cout<<res/2<<"\n";
return ;
}
2025.10.28
CF1765H
考虑二分。
那么现在问题变成,每个点有个截止时间 \(p_i\),\(u\) 需要在 \(v\) 之前访问,求是否可行。
有个显然的贪心是把图建出来,记 \(mi_i\) 为 \(i\) 能到的点中,\(p_j\) 的最小值。然后跑拓扑排序,队列中元素按照 \(mi_i\) 升序。
这个的时间复杂度 \(O(n(n+m)\log^2n)\),显然炸。
简单观察,发现 \(mi_i \ge \max(mi_j[j \to i])\),也就是每次加进去的 \(mi_v\) 不比当前的 \(mi_u\) 小。那么维护队列里 \(mi_i=x\) 的 \(i\) 的集合,每次只要集合中没有数就往大的找。时间复杂度 \(O(n(n+m)\log n)\)。
AT_arc122_d
困难。
如果最优决策下,\((i,j)\) 为一对。那么无论 A 怎么选,\(B\) 都能配对。答案也就是 \(\max (i \oplus j)\)。所以问题等价于,求最小的 \(x\),使得将 \(A\) 分成 \(n\) 组后,\(\max (A_i \oplus A_j) \le x\)。不能二分。
从高往低考虑。对于 \(x\)。记 \(s1\) 为前缀为 \(x\) 的数中,下一位为 \(1\) 的数的集合,\(s0\) 为下一位为 \(0\) 的数的集合。
如果 \(s1\) 是偶数,那么可以通过 \(s0\) 中任意配对,\(s1\) 中任意配对使下一位为 \(0\)。且 \(s0\) 与 \(s1\) 配对一定会使下一位为 \(1\),不优。那么拆成两个新的 \(x1,x2\) 与两个新的集合 \(s1,s2\)。继续执行。
如果 \(s1\) 是奇数。那么必然会有一个和 \(s0\) 中的数配对。对于剩下的数,因为下一位是 \(0\),所以不可能大于 \(s1\) 与 \(s0\) 中两个数配对得到的异或和。那么只需要让这两个数的异或和最小。Trie 维护。
由于每个数对多插入或查询一次,所以时间复杂度 \(O(n\log V)\)。
2025.10.29
AT_arc117_d
如果是一条链怎么做。
那么就是对于任意 \((i,j)\),\(|e_i-e_j|\ge |i-j|\)。构造 \(1,2,\dots, n\) 即可。
好像没用。如果不是链怎么做。
\(dis(u,v)=dep_u+dep_v-2dep_{lca}\)。那么有:
显然答案上界是 \(\max (dep_u)\)。因为 \(e_u=dep_u\) 一定行。
将 \(e_i\) 从小到大排序。
有 \(dis(a,b)+dis(b,c) \ge dis(a,c)\),在 \(b \in P(a,c)\) 时取等。而 \((e_i-e_{i-1})+(e_{i+1}-e_i)=e_{i+1}-e_{i-1}\),所以只需要满足相邻两个就行了。
问题变成,构造 \(e\),使得 \(e_{k_i}-e_{k_{i-1}}\ge dis(k_i,k_{i-1})\),且 \(e_{k_n}\) 最小。
发现实际上是从 \(k_1\) 出发,走到 \(k_n\)。也就是说,\(\sum dis(k_i,k_{i+1})\) 不小于 \(2(n-1)-dis(k_1,k_n)\)。那么 \(\sum e_{k_i}-e_{k_{i-1}}=e_{k_n}-e_{k_1} \ge 2(n-1)-dis(k_1,k_n)\)。在 \(k_{1}\dots k_n\) 构成 DFS 序取等。
显然 \(e_{k_1}=1\)。则 \(e_{k_n}= 2(n-1)-dis(k_1,k_n)+1\)。要让 \(e_{k_n}\) 小,就要让 \(dis(k_1,k_n)\) 大。\(dis(k_1,k_n)\) 不大于树的直径。
那么就是求直径上两个点,然后构造 DFS 序,使得直径上两点分别为起点,终点。时间复杂度 \(O(n)\)。
AT_arc112_c
定义状态函数 \(f_u\) 先手在 \(u\) 为根的子树中能得到的最多金币数量。
那么 \(u\) 这个点会先贡献 \(1\)。变成后手操作。
对于一棵子树 \(u\),从 \(u\) 开始到回到 \(u\),中途会有 \(2(siz_u-1)+siz_u\) 次操作。也就说,如果已知进入时是先手还是后手,那么就可以知道返回时是先手还是后手。具体地,有:
- \(siz_v\) 是偶数。如果当前在 \(u\) 的是先手,那么后手走到 \(v\),变成先手,操作偶数次,回到 \(v\) 是先手,回到 \(u\) 是后手。如果是后手,那么回到 \(u\) 是先手。
- \(siz_v\) 是奇数。如果当前是先手,结束是先手;如果当前是后手,结束是后手。
现在在 \(u\) 的时候是后手先走,那么如果存在 \(siz_v\) 是偶数,先走不劣,因为可以继续得到 \(f_v\)。当然也可能走一个 \(siz_v\) 是奇数,但 \(f_{v}\) 很大的。发现后面那个显然可以在走完偶数后再走。则现在变成先后手交替的过程了。每个人都会选择当前剩下的点中 \(f_{v}\) 最大的。优先队列或排序后维护即可。时间复杂度 \(O(n\log n)\)。
哦,有点问题。这个只考虑了对决策者自己的贡献,没考虑另一个人得到的硬币数量。对于 \(siz_v\) 是偶数,自己可以获得 \(f_{v}\),另一个人会获得 \(siz_v-f_v\)。有:
- \(2f_v \ge siz_v\),延续之前的决策,后手直接选完。
- \(2f_{v}<siz_v\),让另一个人选自己可以获得更多。这个时候只有通过 \(siz_{v}\) 是奇数的点来交替。但是发现如果后手交换成先手,先手也会在存在 \(siz_v\) 是奇数的点的情况下继续交替。
可以发现,在选完 \(siz_v\) 是偶数且 \(2f_v\ge siz_v\) 和 \(siz_v\) 是奇数的点后才会给某一个人剩下所有点。因为如果当前存在 \(siz_v\) 是奇数,且 \(2f_v \ge siz_v\),选则不劣。如果不存在,当存在 \(siz_v\) 是奇数时(\(2f_v < siz_v\)),如果选择那些点,那么要么剩下的所有点都选在选 \(siz_v\) 是奇数的,要么选一部分再通过 \(siz_v\) 是奇数的交换。显然无论哪种都是不优于直接选 \(siz_v\) 是奇数的。
那么决策如:
- 后手将所有 \(siz_v \bmod 2=0 \land 2f_v \ge siz_v\) 的选完。
- 后手,先手,后手……轮流选,每次选择 \(siz_v\bmod 2=1\) 中 \(siz_v-2f_v\) 尽量大的,因为尽量保持差距不劣。
- 剩下所有 \(siz_v \bmod 2=0 \land 2f_v < siz_v\) 的点给当前决策者。
用优先队列或排序维护即可。时间复杂度 \(O(n\log n)\)。
哦,进到 \(v\) 的点是先手不是后手,因为后手是走到 \(v\),不过没关系。
il void dfs(int u){
int st=0,x=1,y=0;
int rx=0,ry=0;
siz[u]=1;
for(auto v:e[u]) dfs(v),siz[u]+=siz[v];
priority_queue<pii> qu;
for(auto v:e[u]){
if(siz[v]&1) qu.push({siz[v]-2*f[v],v});
else{
if(2*f[v]<=siz[v]) x+=f[v],y+=siz[v]-f[v];
else rx+=f[v],ry+=siz[v]-f[v];
}
}
while(!qu.empty()){
int v=qu.top().y;qu.pop();
if(st==0) x+=f[v],y+=siz[v]-f[v];
else y+=f[v],x+=siz[v]-f[v];
st^=1;
}
if(st==0) x+=rx,y+=ry;
else y+=rx,x+=ry;
f[u]=x;
return ;
}
2025.10.30
CF864F
*2700 和一位。
倍增,对于终点相同的,暴力处理每个点走 \(2^i\) 步后会到哪个点。那么无解的情况就是走若干步后仍然不停下。时间复杂度 \(O(n^2\log n)\)。
AT_arc115_d
如果是棵树怎么做。打暴力 DP 发现 \(n\) 个点的树,有 \(k\) 个奇数点的方案数是 \(C_{n}^k\)。为啥呢,因为如果先确定一些点是奇数点。那么从叶子开始保留边,如果叶子是奇数点,则需要保留与其父亲的边,否则不能。在继续往上推。那么每条的状态就是一定的。
那如果是无向图呢。考虑一个联通块。还是依据树来做吧。因为在点状态一定的时候,一定可以通过上面的模拟得到一组唯一的构造。所以对于非树边,如果我们确定那 \(k\) 个奇数点,再确定哪些边要保留,就变成确定 \(k'\) 个奇数点的情况了。所以答案是 \(2^{m-(n-1)}C_{n}^k\)。
如果不是一个联通块。考虑 DP。定义状态函数 \(f_{i,j}\) 表示前 \(i\) 个联通块,有 \(j\) 个奇数点的方案数。时间复杂度 \(O(n^3)\)。真的吗?注意到联通块大小和 \(\sum siz_i=n\),那么每次 \(O(siz_i \times n)\) 转移实际上是 \(O(n^2)\) 的。要注意,选不出奇数个奇数点,因为度数和为偶数。

浙公网安备 33010602011771号