Codeforces Round 855 (Div. 3) A~E

A - Rudolf and the Ticket

鲁道夫要去拜访伯纳德,他决定乘坐地铁去找他。车票可以在接受两个硬币的机器上购买,这两个硬币的总和不超过 $k$ 。

鲁道夫有两个装硬币的口袋。左边口袋里有 $n$ 枚面值为 $b_1, b_2, \dots, b_n$ 的硬币。右边口袋里有 $m$ 枚面值为 $c_1, c_2, \dots, c_m$ 的硬币。他想从左边口袋和右边口袋各取出一枚硬币(共两枚)。

请帮助鲁道夫判断有多少种方法可以选择指数 $f$ 和 $s$ ,从而使 $b_f + c_s \le k$ .


水题,对任意一个口袋的硬币排序后直接遍历另一个口袋,upper_bound收集答案即可。

void solve(){
    CIN(n);CIN(m);CIN(k);
    vector<int> a,b;
    FOR(i,1,n){
        CIN(tmp);a.push_back(tmp);
    }
    FOR(i,1,m){
        CIN(tmp);b.push_back(tmp);
    }
    sort(b.begin(),b.end());
    ll ans=0;
    for(auto i:a){
        ans+=upper_bound(b.begin(),b.end(),k-i)-b.begin();
    }
    cout<<ans<<ET;
}

B - Rudolf and 121

Rudolf 有一个由 $n$ 个整数组成的数组 $a$ ,元素的编号从 $1$ 到 $n$ 。

在一次操作中,他可以选择索引 $i$ ( $2 \le i \le n - 1$ ) 并赋值:

  • $a_{i - 1} = a_{i - 1} - 1$
  • $a_i = a_i - 2$
  • $a_{i + 1} = a_{i + 1} - 1$

鲁道夫可以任意多次使用这个运算。任何索引 $i$ 都可以使用 0 次或更多次。

他能用这个运算使数组中的所有元素都等于零吗?


还是水题,贪心即可。注意不合法的判断。

void solve(){
    CIN(n);
    vector<int> a;a.push_back(0);
    FOR(i,1,n){
        CIN(tmp);a.push_back(tmp);
    }
    FOR(i,2,n-1){//一开始只写了a[i]<0,被卡了
        if(a[i-1]<0||a[i]<0||a[i+1]<0){cout<<"NO"<<ET;return;}
        a[i+1]-=a[i-1];
        a[i]-=2*a[i-1];
        a[i-1]=0;
    }
    FOR(i,1,n){
        if(a[i]!=0){cout<<"NO"<<ET;return;}
    }
    cout<<"Yes"<<ET;
}

C - Rudolf and the Ugly String

题意:最少删除几次可以把map和pie全删掉?

暴力就能过。

void solve(){
    CIN(n);
    string s;cin>>s;s=" "+s;
    if(n<3){
        cout<<0<<ET;return;
    }
    ll mapc=0,piec=0,mapie=0;
    FOR(i,3,n){
        if(s[i-2]=='m'&&s[i-1]=='a'&&s[i]=='p')mapc++;
        if(s[i-2]=='p'&&s[i-1]=='i'&&s[i]=='e')piec++;
    }
    FOR(i,5,n){
        if(s[i-4]=='m'&&s[i-3]=='a'&&s[i-2]=='p'&&s[i-1]=='i'&&s[i]=='e')mapie++;
    }
    cout<<mapc+piec-mapie<<ET;
}

翻到的别人的代码(按自己的马蜂抄了一遍):

void solve(){
    CIN(n);
    string s;cin>>s;s=" "+s;
    if(n<3){cout<<0<<ET;return;}
    ll ans=0;
    FOR(i,1,n-2)
        if(s.substr(i,3)=="map"||s.substr(i,3)=="pie"){ans++;i+=2;}
    cout<<ans<<ET;
}

D - Rudolf and the Ball Game

超级阅读题,看懂了就知道其实很简单,因为记不清方向,只记得传递间隔,问你最后这个球可能在哪些人手上,显然在每次忘记时分裂成两个方向,分开统计就行。自然而然就是DFS(写BFS应该也行)。有枝你不剪?记得剪枝

bool vis[1002][1002];
void solve(){
    CIN(n);CIN(m);CIN(i);
    map<int,bool> could;
    vector<pair<int,char>> a;
    FOR(i,1,m){
        CIN(tmp);char c;cin>>c;
        a.push_back({tmp,c});
    }
    memset(vis,0,sizeof vis);
    auto dfs=[&](auto dfs,int now,int cnt){
        now--;
        now=(now%n+n)%n;//经典二连取模,保证now非负且在0到n内
        now++;
        if(vis[cnt][now])return;
        vis[cnt][now]=1;
        if(cnt==0){
            could[now]=1;
            return;
        }
        if(a[m-cnt].second=='0'){
            dfs(dfs,(now+a[m-cnt].first-1)%n+1,cnt-1);
        }else if(a[m-cnt].second=='1'){
            dfs(dfs,(now-a[m-cnt].first-1)%n+1,cnt-1);
        }else{
            dfs(dfs,(now+a[m-cnt].first-1)%n+1,cnt-1);
            dfs(dfs,(now-a[m-cnt].first-1)%n+1,cnt-1);
        }
    };
    dfs(dfs,i,m);
    cout<<could.size()<<ET;
    //for(auto &[i,v]:could)cout<<i<<"]";
    FOR(i,1,n){
        if(could.count(i))cout<<i<<' ';
    }
    cout<<ET;
}

E - Rudolf and k Bridges

伯纳德喜欢拜访鲁道夫,但他总是迟到。问题是伯纳德必须乘渡船过河。鲁道夫决定帮助他的朋友解决这个问题。

这条河是由 $n$ 行和 $m$ 列组成的网格。第 $i$ 行和第 $j$ 列的交点包含数字 $a_{i,j}$ –相应单元格的深度。第一列和最后列中的所有单元格都与河岸相对应,因此它们的深度为 $0$ 。

河流可能是这样的。

鲁道夫可以选择第 $(i,1), (i,2), \ldots, (i,m)$ 行,并在上面建造一座桥。在该行的每个单元格中,他都可以为桥安装一个支架。在第 $(i,j)$ 格安装支架的成本为 $a_{i,j}+1$ 。安装支架必须满足以下条件:

  1. 必须在单元格 $(i,1)$ 中安装一个支架;
  2. 单元格 $(i,m)$ 中必须安装一个支架;
  3. 任何一对相邻支架之间的距离不得大于** $d$ 。支架 $(i, j_1)$ 和 $(i, j_2)$ 之间的距离为 $|j_1 - j_2| - 1$ 。

只建一座桥是很无聊的。因此,鲁道夫决定在河上连续行建造 $k$ 座桥,即选择一些 $i$ ( $1 \le i \le n - k + 1$ ),独立建造 $k$ 座桥。( $1 \le i \le n - k + 1$ ) 并分别在每一行的 $i, i + 1, \ldots, i + k - 1$ 上建一座桥。帮助鲁道夫最小化安装支架的总成本。


阅读题,读懂了就知道直接对每行都跑一次单调队列优化DP,再前缀和查最小答案就行了。(特别注意那个连续,要是我注意到了,估计这场就能过5题了,可惜了,最气人的一场。)

struct P{
    ll i,v;
    bool operator<(const P &b)const{
        if(v==b.v)return i<b.i;
        return v>b.v;
    }
}t;
void solve(){
    CIN(n);CIN(m);CIN(d);CIN(k);
    vector<ll> ans;ans.push_back(0);
    FOR(i,1,n){
        vector<ll> a;a.push_back(0);
        FOR(j,1,m){
            CIN(tmp);
            a.push_back(tmp);
        }
        priority_queue<P> q;
        q.push({1,1});
        FOR(i,2,m){
            while(!q.empty()&&k<i-q.top().i-1)q.pop();
            //cout<<q.top().v<<"]";
            if(i!=m)q.push({i,a[i]+1+q.top().v});
            else ans.push_back(a[i]+1+q.top().v);
        }
        //cout<<ET;
    }
    ll anss=1e18;
    vector<ll> pre;pre.push_back(0);
    FOR(i,1,n)pre.push_back(pre[i-1]+ans[i]);
    FOR(i,d,n)anss=min(anss,pre[i]-pre[i-d]);
    //sort(ans.begin(),ans.end());//reverse(ans.begin(),ans.end());
    //FOR(i,0,d-1)anss+=ans[i];
    cout<<anss<<ET;
}

附录

这里贴上头部定义和main函数部分:

#include<iostream>
#include<vector>
#include<bits\stdc++.h>
#define ll long long
#define ld long double
using namespace std;
#define ET '\n'
#define FOR(i,a,b) for(ll i=(a);i<=(b);i++)
#define rFOR(i,a,b) for(ll i=(a);i>=(b);i--)
#define CIN(a) ll a;cin>>a
#define DCIN(a) ld a;cin>>a
#define CA cout<<ans<<ET
#define CY cout<<"YES"<<ET
#define CN cout<<"NO"<<ET
//没有C++20就不能用ranges了
//#define PP(l,r,CK) *ranges::partition_point(ranges::iota_view((l),(r)+1),(CK))
string ANS[2]={"No\n","Yes\n"};

//solve函数代码

int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    //pre();
    CIN(t);while(t--)
    solve();
    return 0;
}