板刷 CF 记录

全部为 EDU 场次。

CF EDU 157

T1

首先明确,必须要到 \(y\),所以答案至少是 \(y\),发现还有箱子这一回事,考虑分类讨论,如果我们先碰到钥匙,就可以直接带着钥匙去找箱子。如果先遇到箱子,就能搬几步,剩下去到钥匙拿回来即可。

感觉没啥 trick 或者好总结的,就是需要注意到必须要到 \(y\),感觉挺显然的。评分 *\(800\) 分。

T2

考虑到这个式子,只是 \(x\)\(x\) 或者 \(y\)\(y\),所以直接全拍了,然后分别维护两个指针移动统计即可。

我们尝试证明一下排序是对的,绝对值的几何意义是两数的差距,那么我们希望每两个数的差距都尽可能的小,则排序是最优的。然后考虑为什么要整个排序,还是考虑交换,只会使得差距增大,然后就证出来了。

这个排序缩小查询是一个非常简单的 trick,然后没了。

T3

简单题。考虑对于每一个字符串,我们枚举每个前缀作为一半的情况,然后就是找一个数,能使得它的后本部分加上这整个串的和等于前半段,还有一个限制是长度需要相等。我们考虑 vector,把所有长度 \(=x\) 的值丢进 v[x],查询时直接二分即可。

重点还是 vector 分类这个 trick 吧。

T4

牛牛题。我的思路就是,考虑 \(0\) 是关键,假设 \(0\)\(i\) 位置,我们枚举 \(i\),前面的就是 \(b\) 所有以 \(i\) 结尾的所有后缀异或和,后面类似,以 \(i+1\) 为前缀的所有前缀异或和。

然后这里有一个关键转化/trick:我们只需要后面的 \(b\) 与前面 \(b\) 均小于 \(n\),就能把所有 \(b\) 都控制在 \(0\)~\(n-1\) 内。注意到有所有 \(b\) 不同,因为题目保证有解,则只要最大的 \(b\) 小于 \(n\),所有又不同,就会刚好被映射进 \(0\)~\(n-1\)

还需要有一个步骤,我们如何维护这个最大值呢?我们假设要求前缀,后缀直接仿照做就行。考虑我们把 \(i\) 前面所有的前缀异或和插进一个 0/1 Tie 里。然后假设一个区间对应 \([1,p]\),我们考虑给异或上一个 \(1\)\(i\) 的异或和,把它设为 \(x\),现在就会变成 \([p+1,n]\) 这个区间的异或和。那么就只要在 01 Trie 里面查询与 \(x\) 异或的最大值即可。码量很大。

然后考虑另外一种更简单的做法,我们简单手玩发现 \(b_1\) 异或上 \(b_{i+1}\) 就等于 \(1\)\(j\) 的前缀异或和。把这些前缀异或和插入 01 Trie,然后根据前面的结论,就是让这些里面的最大值最小,枚举 \(b_1\) 在 01 Trie 上判断即可

妙。

#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';
    }
}
posted @ 2026-03-05 21:08  Cefgskol  阅读(6)  评论(0)    收藏  举报