跳转至

比赛时间: 2025-03-01

AtCoder Beginner Contest 395

赛时只出了ABCD,E其实也只是个绿题,刚才补的时候倒是很快就A了,赛时写得时候没多少时间了,且方法错了,我怎么上来直接写dfs,想吃TLE了。

A

直接判断就行了。我没注意还不能相等,然后用了is_sorted(),且只测了前两个样例喜提WA。

void solve()
{
    int n;
    cin>>n;
    vector<int>a(n);
    for(int i=0;i<n;i++)
        cin>>a[i];
    for(int i=1;i<n;i++){
        if(a[i]<=a[i-1]){
            cout<<"No\n";
            return;
        }
    }
    cout<<"Yes\n";
}

B

直接模拟就行了。

void solve()
{
    int n;
    cin>>n;
    vector<vector<char>>s(n+1,vector<char>(n+1));
    for(int i=1;i<=n;i++){
        int j=n-i+1;
        if(j<i) continue;
        for(int k=i;k<=j;k++)
            for(int l=i;l<=j;l++){
                if(i&1) s[k][l]='#';
                else s[k][l]='.'; 
            }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cout<<s[i][j];
        }
        cout<<'\n';
    }
}

C

记一下上一次出现的位置,然后每次遇到就更新就行了。

constexpr int N = 1e6;

int lst[N+1];

void solve()
{
    int n;
    cin>>n;
    vector<int>a(n+1);
    int ans=n+1;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        if(lst[x]){
            ans=min(ans,i-lst[x]+1);
        }
        lst[x]=i;
    }
    cout<<(ans==n+1?-1:ans)<<'\n';
}

D

稍微有点操作啊,控了我一个小时。

其实主要就是思路没想明白,非常的混乱,现在来稍微整理一下。

这个操作 \(2\) 需要交换 \(a\)\(b\) 里面装的鸟。我们肯定不能真的去换,因为更新每个鸟的家复杂度非常高。

我们可以只改变它们代表的值,比如说原本每个人的家就是 \(i\) ,那我现在交换 \(1\)\(2\) ,怎么能使得不用改每一个成员的家呢?

我们其实相当于可以只用改这两个巢的编号,比如说我们要交换 \(a\)\(b\) 这两个巢的内容,那其实也相当于把 \(a\) 的编号改成 \(b\)\(b\) 改成 \(a\)

我们可以先设 \(home_i\) 代表的是第 \(i\) 只鸟的家。假设我们需要交换 \(1\)\(2\) ,我们可以想办法修改它们映射的值。即用一个数组表示原始的第 \(i\) 个巢穴现在它的位置是多少。比如 \(p_i\) 代表第 \(i\) 个巢穴的位置。

那么我们要输出 \(i\) 的家需要输出 \(p_{home_i}\)\(home_i\) 代表第 \(i\) 只鸟,它现在的家的最开始的位置。然后 \(p_i\) 就可以表示当初第 \(i\) 个位置的巢现在所在的位置。

那么我们如果要交换两个巢,只需要交换它们对应的 \(p\) 值。

但是我们需要交换巢 \(a\)\(b\) 时,这里给的是巢的位置,我们的 \(p\) 记的是最开始的位置的巢的现在的位置。所以我们还需要记一下每个位置当前放的是哪一个巢。

这个更简单实现,直接用一个 \(pos\) 数组实现,只有操作 \(2\) 会移动鸟巢。那么我们就交换 \(pos_a\)\(pos_b\) 代表就换 \(a\)\(b\) 位置上的鸟巢。

然后再交换 \(home_{pos_a}\)\(home_{pos_b}\) ,即交换两个实际的鸟巢的对应的值。

void solve()
{
    int n,q;
    cin>>n>>q;
    vector<int>home(n+1);
    vector<int>p(n+1),pos(n+1);
    for(int i=1;i<=n;i++)
        home[i]=p[i]=pos[i]=i;
    while(q--){
        int op;
        cin>>op;
        if(op==1){
            int a,b;
            cin>>a>>b;
            home[a]=pos[b];
            //我们还得知道 哪个pi 等于 b...
            //这样好像也不对,纯乱写了一坨。。。
        }else if(op==2){
            int a,b;
            cin>>a>>b;
            //p[30]不就是8吗?
            swap(p[pos[a]],p[pos[b]]);
            //p[rev[b]] p[xx]=b
            swap(pos[a],pos[b]);
            // cerr<<p[a]<<" "<<p[b]<<'\n';
            // rev[p[p[a]]]=p[a],p[p[b]]=p[b];
        }else{
            int a;
            cin>>a;
            cout<<p[home[a]]<<'\n';
        }
        // for(int i=1;i<=n;i++)
        //     cerr<<p[i]<<" \n"[i==n];
        // for(int i=1;i<=n;i++)
        //     cerr<<rev[i]<<" \n"[i==n];
    }
    // cerr<<rev[7]<<" "<<rev[8]<<" "<<rev[30]<<'\n';
}

E

感觉确实 dijkstra 板题。只是稍微记一下每一个点的状态就行了。

void solve()
{
    int n, m, x;
    cin >> n >> m >> x;
    set<pair<int, int>> edges;
    vector<vector<int>> adj(n + 1, vector<int>());
    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        edges.insert({ u, v });
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    vector<long long> cost(n + 1, 1e18);
    vector<vector<long long>> dis(n + 1, vector<long long>(2, 1e18));
    dis[1][0] = 0;
    auto dijkstra = [&]() {
        priority_queue<array<long long, 3>, vector<array<long long, 3>>, greater<>> pq;
        pq.push({ 0, 1, 0 }); // 距离 编号 状态
        while (!pq.empty()) {
            auto [d, u, rev] = pq.top();
            pq.pop();
            if (d > dis[u][rev])
                continue;
            for (auto v : adj[u]) {
                if (rev) {
                    if (edges.contains({ v, u })) {
                        if (dis[u][rev] + 1 < dis[v][rev]) {
                            dis[v][rev] = dis[u][rev] + 1;
                            pq.push({ dis[v][rev], v, rev });
                        }
                    } else {
                        if (dis[u][rev] + x + 1 < dis[v][rev ^ 1]) {
                            dis[v][rev ^ 1] = dis[u][rev] + x + 1;
                            pq.push({ dis[v][rev ^ 1], v, rev ^ 1 });
                        }
                    }
                } else {
                    if (edges.contains({ u, v })) {
                        if (dis[u][rev] + 1 < dis[v][rev]) {
                            dis[v][rev] = dis[u][rev] + 1;
                            pq.push({ dis[v][rev], v, rev });
                        }
                    } else {
                        if (dis[u][rev] + x + 1 < dis[v][rev ^ 1]) {
                            dis[v][rev ^ 1] = dis[u][rev] + x + 1;
                            pq.push({ dis[v][rev ^ 1], v, rev ^ 1 });
                        }
                    }
                }
            }
        }
    };
    dijkstra();
    cout << min(dis[n][0], dis[n][1]) << '\n';
}

明天再把 F 看了,是个青题。

F

看出来了是二分,但是check不太会写。

我本来记每个的 [l,r] ,然后看每个相邻的能不能相交,但我判的 [l,r] 就是简单的看它自己的高度。其实可以整体考虑。

用一个 \([L,R]\) 来记一下当前的合法的区间,每次可以被扩展到 \([L-x,R+x]\) , 然后当前的 \(u\) 的范围是 \([max(0,H-d),U]\)

一直扩展区间然后判有没有交集就可以了。

void solve()
{
    int n, x;
    cin >> n >> x;
    vector<int> u(n), d(n);
    for (int i = 0; i < n; i++)
        cin >> u[i] >> d[i];
    long long lo = 0, hi = 2e9 + 1;
    auto check = [&](long long H) {
        long long L = 0, R = H;
        for (int i = 0; i < n; i++) {
            //:之前的区间 [L-X,R+X]
            //:当前的区间 [max(0,H-D),U]
            L = max({L-x,H-d[i],0LL});
            R = min(R + x, 1LL*u[i]);
            if(L>R) return false;
        }
        return true;
    };
    while (lo < hi - 1) {
        long long mid = (lo + hi) >> 1;
        if (check(mid))
            lo = mid;
        else
            hi = mid;
    }
    // cout<<check(2)<<'\n';
    long long ans = 0;
    for (int i = 0; i < n; i++) {
        ans += u[i] + d[i] - lo;
    }
    cout << ans << '\n';
}