做题
CF EDU 157
T1
首先明确,必须要到 \(y\),所以答案至少是 \(y\),发现还有箱子这一回事,考虑分类讨论,如果我们先碰到钥匙,就可以直接带着钥匙去找箱子。如果先遇到箱子,就能搬几步,剩下去到钥匙拿回来即可。
感觉没啥 trick 或者好总结的,就是需要注意到必须要到 \(y\),感觉挺显然的。评分 *\(800\) 分。
T2
考虑到这个式子,只是 \(x\) 减 \(x\) 或者 \(y\) 减 \(y\),所以直接全拍了,然后分别维护两个指针移动统计即可。
我们尝试证明一下排序是对的,绝对值的几何意义是两数的差距,那么我们希望每两个数的差距都尽可能的小,则排序是最优的。然后考虑为什么要整个排序,还是考虑交换,只会使得差距增大,然后就证出来了。
这个排序缩小查询是一个非常简单的 trick,然后没了。
T3
简单题。考虑对于每一个字符串,我们枚举每个前缀作为一半的情况,然后就是找一个数,能使得它的后本部分加上这整个串的和等于前半段,还有一个限制是长度需要相等。我们考虑 vector,把所有长度 \(=x\) 的值丢进 v[x],查询时直接二分即可。
重点还是 vector 分类这个 trick 吧。
T4
upd 2026.8.22 ::修了一下这个题,之前我写的怎么又狗屎又雾。。。
牛牛题。我的思路是你考虑 \(0\) 的位置很关键,你手玩一下,假设第 \(b_i=0\),然后 \(b_{i}\oplus b_{i+1}=a_i\),所以 \(b_{i+1}=a_i\),类似的,\(b_{i+1}\oplus b_{i+2}=a_i\),所以 \(b_{i+2}=a_i\oplus a_{i+1}\),发现 \(0\) 两侧的 \(b_i\) 就是一个往两边的前缀和后缀异或和。因为题目保证有解,我们可以做一步转化就是你只要考虑让 \(b_i\) 最小就是对的。我们枚举 \(0\) 的位置 \(i\),只要其对应的 \(b\) 的最大值 \(<n\) 就可以,那么你异或最大值你上一个 0/1 Trie 考虑,比如说你先考虑左边的最大值,右边的类似维护,发现我们只需要往 0/1 Trie 里插入每个形如 \([1,p]\) 的异或和,此时你只要查询 Trie 里与 \([1,i]\) 这段的异或和的最大值就可以,因为 \([1,p]\oplus [1,i]=[p+1,i]\),然后就做完了。
考虑另外一种更为简洁的做法,注意到 \(\oplus_{i=1}^{j} a_i=b_1 \oplus b_{j+1}\),然后问题就变成我要找到一个能够使 \(b\) 的值最小的 \(b_1\),我们记 \(a_i\) 的前缀异或和为 \(s_i\),我们枚举 \(b_1\),插入所有的 \(s_i\),如果这个 \(b_1\) 能够使得查询出来的最大值 \(<n\) 说明它是合法的,那么就做完了。
妙。
#include<bits/stdc++.h>
using namespace std;
int sum[200005],trie[6200005][2],a[200005],tot;
void insert(int x){
int p=0;
for (int i=30;i>=0;--i){
int a=(x>>i)&1;
if (!trie[p][a])trie[p][a]=++tot;
p=trie[p][a];
}
}
int query(int x){
int p=0,ans=0;
for (int i=30;i>=0;--i){
int a=(x>>i)&1;
if (trie[p][!a])ans+=(1<<i),p=trie[p][!a];
else p=trie[p][a];
}
return ans;
}
int main(){
int n;cin>>n;
for (int i=1;i<n;++i){
cin>>a[i];
sum[i]=sum[i-1]^a[i];
}
for (int i=0;i<n;++i)insert(sum[i]);
int b=0;
for (int i=0;i<n;++i){
if (query(i)<n){b=i;break;}
}
for (int i=0;i<n;++i)cout<<(b^sum[i])<<" ";
return 0;
}
T5
emm... 2300 感觉很困难。
我们称先手为 A,后手为 B。对于对方打出了一张牌,我们肯定选择一个攻击力能够管上,然后防御值尽可能大的牌,这样能尽可能压缩对手的出牌空间,所以对于每张牌队手管上的牌是一定的。然后考虑一个 DAG 博弈论模型,我们把每张牌视作一个节点,然后把牌和管它的牌连一条有向边边。如何连边呢?我们按照攻击力排序,枚举每张牌,然后二分找到对面牌池中第一个能够管上的牌,此时一定连向防御值的最大后缀值。
然后有一个结论是如果某一个节点出度是 \(0\),这个点就是能够令 A 必胜的。考虑经典博弈结论,必胜点一定连向必败点,必败点一定连向必胜点,那么先初始化必胜点状态,然后一边 DFS 即可。
代码很长。
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int posa[N],posb[N],ta[N],tb[N];
struct KKK{
int x,y;
}a[N],b[N];
bool cmp(KKK p,KKK q){
return p.x<q.x;
}
struct edge{
int to,nxt;
}e[N*2];
int hd[N*2],tot;
void add(int u,int v){
e[++tot].to=v;
e[tot].nxt=hd[u];
hd[u]=tot;
}
int n,m;
int find1(int x){
int ans=0;
for (int i=30;i>=0;--i){
int y=(1ll<<i);
if (ans+y>n)continue;
if (a[ans+y].x<=x)ans+=y;
}
if (ans>=n)return -1;
else return ans+1;
}
int find2(int x){
int ans=0;
for (int i=30;i>=0;--i){
int y=(1ll<<i);
if (ans+y>m)continue;
if (b[ans+y].x<=x)ans+=y;
}
if (ans>=m)return -1;
else return ans+1;
}
int ans[N*2],vis[N*2];
void dfs(int u){
vis[u]=1;
for (int i=hd[u];i;i=e[i].nxt){
int v=e[i].to;
if (!vis[v]) dfs(v);
if (ans[v]!=-1) ans[u]=ans[v]^1;
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int T;cin>>T;
while (T--){
cin>>n;
for (int i=1;i<=n;++i)cin>>a[i].x;
for (int i=1;i<=n;++i)cin>>a[i].y;
cin>>m;
for (int i=1;i<=m;++i)cin>>b[i].x;
for (int i=1;i<=m;++i)cin>>b[i].y;
sort(a+1,a+n+1,cmp);
sort(b+1,b+m+1,cmp);
ta[n+1]=0;
for (int i=n;i>=1;--i){
if (a[i].y>ta[i+1]){
ta[i]=a[i].y;
posa[i]=i;
}else{
ta[i]=ta[i+1];
posa[i]=posa[i+1];
}
}
tb[m+1]=0;
for (int i=m;i>=1;--i){
if (b[i].y>tb[i+1]){
tb[i]=b[i].y;
posb[i]=i;
}else{
tb[i]=tb[i+1];
posb[i]=posb[i+1];
}
}
tot=0;
for (int i=1;i<=n+m;++i)hd[i]=0;
for (int i=1;i<=n;++i){
int p=find2(a[i].y);
if (p==-1)continue;
add(i,posb[p]+n);
}
for (int i=1;i<=m;++i){
int p=find1(b[i].y);
if (p==-1)continue;
add(i+n,posa[p]);
}
for (int i=1;i<=n+m;++i){
ans[i]=-1;
vis[i]=0;
}
for (int i=1;i<=n+m;++i){
if (hd[i]==0)ans[i]=1;
}
for (int i=1;i<=n+m;++i){
if (!vis[i])dfs(i);
}
int aa=0,bb=0,cc=0;
for (int i=1;i<=n;++i){
if (ans[i]==1)aa++;
else if (ans[i]==0)bb++;
else cc++;
}
cout<<aa<<" "<<cc<<" "<<bb<<'\n';
}
}
CF EDU 47
T2
怎么 B 这种签都只会暴力线段树。。。
考虑,\(1\) 是自由的,\(0,2\) 相对关系是一定的,就考虑怎么放全部的 \(1\),容易得到,放到第一个 \(2\) 之前即可。
感觉没做出来就是没想到这个 \(1\) 是可以随便动这个性质,下次先考虑单体的特殊性质,如可移动性和固定性。
T3
感觉上来猜了个结论就出来了,一定在中间。我们来简单证明一下,题目本质就是让数组 \(a\) 总和最大,\(+x\) 是不影响的,然后就考虑那个距离的东西,关注到我们从 \(i\to i+1(i+1 \le \frac{n}{2})\) 这个绝对值会增加一个右边的长度,减去左边的长度,于是我们考虑调节,到中间就是最优的。这题做完了。
T4
不知道 hyw 题目。我们的目标就是凑够 \(m\) 条边,于是首先可以先从 \(1\) 连出 \(n-1\) 条边,这样保证联通还一定是合法的,然后考虑直接枚举互质,直到找到 \(m\) 条边,最后输出。不会证复杂度,但是可以简单猜测,发现互质数分布密度较高,所以根本跑不满。
然后这题有一个严格 \(O(m)\) 的做法,考虑有一个 trick 就是对于一个数 \(x\),它的互质数是按有周期分布的,我们把与其互质表示为 \(1\),不互质表示为 \(0\),这个 01 串不仅有周期,还知道这个周期的大小是 \(x\)。只知道这个结论,具体做法还不会。记得回来补。
T5
感觉这场比赛全是很难绷的题。
首先发现这个期望没有任何意义,就是所有情况的和,我们按位考虑贡献,假设要算第 \(i\) 位的贡献,然后有一个关键是 \(i\) 前面一定会有 \(a_1\)~\(a_{i-1}\),那么分类讨论看是否在 \(1\) 这个必休息点即可,答案就是两种情况的和。也就是说,答案是
或者还可以用 DP 的方法解释,本质是一样的。
HT-123-Div.2
T1
很典啊,但是 ST1 出这种东西有点过吧。首先这个玩意是个显然 DP,\(f_{i,j,k(0/1)}\) 表示处理了前 \(i\) 位,分出 \(j\) 段,最后一个为 \(k\) 的方案数,你会发现这个东西唯一不好处理的就是这个 \([L,R]\) 的限制,我们考虑刻画一下,不妨钦定现在要转移第 \(i\) 位是 0,我们令最后一段为 \([p+1,i]\),然后考虑首先得到一个东西就是这连着一段全都是 0 或者 ?,于是我们预处理 \(l_i\) 表示前缀 \([1,i]\) 中最后的一个 1 所在的位置,那么必然有 \(p \ge l_i\),然后有 \(L_0 \le i-(p+1)+1 \le R_0\),也就是 \(i-L_0 \ge p \ge i-R_0\),于是:
\(f_{i,j,1}\) 是对称的。你发现直接转移是 \(O(n^2k)\) 的,考虑优化,前缀和优化即可,首先这个 \(j\) 都是 \(j-1\),上一层 DP 已经确定了,所以直接维护一个 \(s_{0,i}=\sum_{x=0}^{i} f_{x,j-1,0}\),于是 \(f_{i,j,0}=s_{0,i-L_0}-s_{0,\min(i-R_0,l_i)-1}\) 即可。大概青题吧。、
T2
是不是还是很典。你考虑显然的一个事就是先缩边双,那么原图就直接变成了一棵树。然后考虑翻转 \(c_x\) 会产生什么贡献,我们不妨令 \(1\) 为根,设 \(u\) 子树内 \(1\) 的数量为 \(s_u\),整棵树一共有 \(S\) 个 \(1\),再令翻转 \(c_x\) 对 \(1\) 的总数产生的变化为 \(d\),也就是 \(+1\) 或 \(-1\)。此时如果删掉 \(u\) 和 \(fa_u\) 之间的桥,若 \(x\) 在 \(u\) 的子树内,那么两边的 \(1\) 数量分别变成 \(s_u+d\) 和 \(S-s_u\),所以贡献就是 \(|S-s_u-(s_u+d)|=|2s_u-S+d|\);如果 \(x\) 不在 \(u\) 的子树内,则类似得到 \(|2s_u-S-d|\)。于是我们只需要分别对这两种情况取最大值。这里考虑一个很经典的 trick,如果要求一堆 \(|x_i+t|\) 的最大值,那么只需要看这些 \(x_i\) 中的最大值和最小值,所以实际上我们只需要维护对应集合里 \(s_u\) 的最大值和最小值。对于 \(x\) 在子树内的情况,其实就是那些位于根到 \(x\) 路径上的桥,因此从上到下预处理一遍路径上 \(s_u\) 的最大最小值即可。剩下的问题就是处理 \(x\) 不在子树内的情况,换到 \(x\) 的视角来看,就是要求所有不在根到 \(x\) 路径上的桥里 \(s_u\) 的最大值和最小值。这个东西做法很多,你考虑拆一下这个路径,先自底向上处理每棵子树内部的最大最小值,同时为了从父亲转移到某个儿子时排除这个儿子自己的贡献,再额外维护一下次大和次小,然后再自顶向下把祖先其他分支的信息传下来,最后把当前子树内和祖先的旁支两部分合起来,就得到了所有路径外桥的最大最小值,然后就做完了。大概上位蓝吧(?)

浙公网安备 33010602011771号