Educational Codeforces Round 175 (Rated for Div. 2) A/B/C/D

A. FizzBuzz Remixed

原题链接:https://mirror.codeforces.com/contest/2070/problem/A

思路:

打表发现为 \(FizzBuzz\) 的数字有 \(0\)\(1\)\(2\)\(15\)\(16\)\(17\) ... {\(15*n+(0/1/2)\)},前 \(n\) 中的 \(FizzBuzz\) 的数字的个数有:\(\lfloor n/15 \rfloor*3+min(n\)%\(15+1,3)\)

时间复杂度:\(O(1)\)

代码:

#include<bits/stdc++.h>
using namespace std;    

typedef long long ll;
typedef pair<int,int> PII;

const int N=2e5+10;

int n;

void solve(){
    cin>>n;

    cout<<n/15*3+min(n%15+1,3)<<endl;
}

int main() {
    cin.tie(0)->sync_with_stdio(false);
    cout.tie(0);

    int t=1;
    cin>>t;

    while(t--)solve();
}

B. Robot Program

原题链接:https://mirror.codeforces.com/contest/2070/problem/B

思路:

简单来说就是物品在到达 \(0\) 点时,运动序列会初始化,题目就是看物品在有限时间内有多少次经过原点。

先可行性判断:

如果在 \(0\) 点左部,那么看最右能否到达 \(0\) 点,在 \(0\) 点右部,那么看最左能否到达 \(0\) 点,如果不能,那么经过 \(0\) 点次数为 \(0\)。并且还要判断首次到达 \(0\) 点的时间 \(st\) 是否大于 \(k\)。如果 \(st>k\)那么显然无法到达。

再判断到达后能否在 \(0\) 点处循环往复的运动:

即判断在运动序列中是否存在一个前缀,\(L\) 的个数与 \(R\) 的个数相同。若不存在,那么无法在 \(0\) 循环往复的运动,只经过 \(0\) 点一次,否则存在一个最小的时间间隔 \(t\),使得在到达 \(0\) 点后每 \(t\) 的时间间隔就会重新回到 \(0\) 点,那么总的到达次数为:$1+\lfloor(k-st)/t\rfloor $

时间复杂度:\(O(n)\)

代码:

#include<bits/stdc++.h>
using namespace std;    

typedef long long ll;
typedef pair<int,int> PII;

const int N=2e5+10;

int n;

void solve(){
    ll x,k;
    cin>>n>>x>>k;

    string s;
    cin>>s;

    int sum=0,lmx=0,rmx=0;
    for(int i=0;s[i];i++){
        if(s[i]=='L') sum--;
        else sum++;
        lmx=min(lmx,sum);
        rmx=max(rmx,sum);
    }

    if(x>0&&x+lmx>0||x<0&&x+rmx<0){
        cout<<0<<endl;
        return;
    }

    int st=0,t=x;
    for(int i=0;s[i];i++){
        if(s[i]=='L')t--;
        else t++;
        if(t==0){
            st=i+1;
            break;
        }
    }

    if(k<st){
        cout<<0<<endl;
        return;
    }

    sum=0;
    for(int i=0;s[i];i++){
        if(s[i]=='L')sum--;
        else sum++;
        if(sum==0){
            t=i+1;
            break;
        }
    }

    if(t==0) cout<<1<<endl;
    else{
        cout<<1+(k-st)/t<<endl;
    }
}

int main() {
    cin.tie(0)->sync_with_stdio(false);
    cout.tie(0);

    int t=1;
    cin>>t;

    while(t--)solve();
}

C. Limited Repainting

原题链接:https://mirror.codeforces.com/contest/2070/problem/C

思路:

初始格子全为红色,涂色次数有限,每次涂色只能涂连续的一段区域且每个位置只能涂色一次由红色涂为蓝色,最终代价为与要求序列不一致位置的代价的最大值。一眼顶针鉴定为纯纯的二分,每次二分答案然后去判断是否可行。

显然如果可涂色的次数大于等于连续的蓝色部分的个数,显然都符合要求,所以答案为0。如果可涂色次数小于连续蓝色部分的个数,那么我们可能会多涂一部分,即最终应该为红色的位置被涂成蓝色,或者少涂一部分,即最终为蓝色的位置没有被涂还是红色。并且被多涂的红色部分应该夹在两个蓝色部分之间,否则多涂对答案是不优的。

所以我们可以构造一个序列,分别对应: 蓝、红、蓝、... 、红、蓝的连续序列,每部分的数值为对应连续序列中的代价的最大值。那么我们就可以二分答案,每次要求答案的上界,只检查序列中在奇数的位置(即蓝色的位置),记总涂色次数为 \(cnt\),涂色采用贪心策略:若对应蓝色位置代价大于上界,那么我们先检查当前位置的左侧临近的蓝色位置(如果存在),若已涂色且两个位置间红色涂色的代价不超过上界,那么我们直接将这部分与上部分涂色相连,否则,必须在此处进行涂色,\(cnt++\);若对应蓝色位置代价不超过上界,那么我们也去检查它的左侧,若也已涂色,且两部分间红色部分涂色后代价不超过上界,那么显然将这部分也涂色对答案没有影响,所以我们也将他与上部分涂色连接起来,进行标记,否则就不涂色直接跳过(因为这部分即使不涂色也不会超过上界并且能够保证涂色次数最少)。最终我们检查 \(cnt\) 是否超过最大要求的涂色次数 \(k\),如果 \(cnt<=k\) 那么答案可行,否则不可行。

时间复杂度:\(O(c*n*log(10^9))\)

代码:

#include<bits/stdc++.h>
using namespace std;    

typedef long long ll;
typedef pair<int,int> PII;

const int N=2e5+10,mod=998244353;

int n,k;

void solve(){
    cin>>n>>k;
    
    string s;
    cin>>s;
    s=" "+s;

    vector<int> a(n+1);
    for(int i=1;i<=n;i++)cin>>a[i];

    int sum=0;
    bool flag=false;

    vector<int> b;
    int mxr=0,mxb=0;

    for(int i=1;s[i];i++){
        if(!flag&&s[i]=='B'){
            if(sum)b.push_back(mxr);
            sum++;
            flag=true;
            mxr=0;
        }else if(s[i]=='R'){
            if(flag) b.push_back(mxb);
            flag=false;
            mxb=0;
        }
        if(flag) mxb=max(mxb,a[i]);
        else mxr=max(mxr,a[i]);
    }

    if(mxb)b.push_back(mxb);

    if(sum<=k){
        cout<<0<<endl;
        return;
    }

    auto check=[&](int mx)->bool{
        int cnt=0;
        vector<bool> st(n+1,0);

        for(int i=0;i<b.size();i++){
            if(!(i&1)){
                if(b[i]>mx){
                    if(i-2>=0&&st[i-2]&&b[i-1]<=mx){
                        st[i]=true;
                        continue;
                    }
                    cnt++,st[i]=true;
                }else{
                    if(i-2>=0&&st[i-2]&&b[i-1]<=mx){
                        st[i]=true;
                        continue;
                    }
                }
            }
        }
        return cnt<=k;
    };

    int l=1,r=1e9;
    while(l<r){
        int mid=l+r>>1;
        if(check(mid))r=mid;
        else l=mid+1;
    }

    cout<<l<<endl;
}

int main() {
    cin.tie(0)->sync_with_stdio(false);
    cout.tie(0);

    int t=1;
    cin>>t;

    while(t--)solve();
}

D. Tree Jumps

原题链接:https://mirror.codeforces.com/contest/2070/problem/D

思路:

深度为 \(depth\) 的顶点只能转移到深度为 \(depth+1\) 且非子节点的位置(根节点除外)。记 \(f[d]\) 为深度为 \(d\) 的位置的所有可行序列的总方案数,\(g[x]\) 为最终位置为 \(x\) 顶点的合法序列的个数,若 \(y\)\(x\) 的子节点,那么: \(g[y]=f[d_x]-g[x]\)\(f[d_y]=\sum_{i=0}^m g[y_i]\) ,那么答案即为:\(ans=\sum_{i=0}^n f[i]\),我们可以用 \(bfs\)解决。

时间复杂度: \(O(n)\)

代码:

#include<bits/stdc++.h>
using namespace std;    

typedef long long ll;
typedef pair<int,int> PII;

const int N=2e5+10,mod=998244353;

int n;
vector<vector<int>> e;

vector<ll> f,g;

inline void add(int a,int b){
    e[a].push_back(b);
}

void solve(){
    cin>>n;

    e=vector<vector<int>>(n+1);
    f=vector<ll>(n+1,0);
    g=vector<ll>(n+1,0);

    int fa;
    for(int i=2;i<=n;i++){
        cin>>fa;
        add(fa,i);
    }

    f[1]=1,f[2]=e[1].size();
    
    queue<int> q;
    vector<int> depth(n+1,0);

    for(auto x:e[1]){
        g[x]=1;
        depth[x]=2;
        q.push(x);
    }

    while(!q.empty()){
        auto x=q.front();
        q.pop();

        int dx=depth[x];

        for(auto y:e[x]){
            g[y]=(f[dx]-g[x]+mod)%mod;
            depth[y]=dx+1;
            f[dx+1]=(f[dx+1]+g[y])%mod;
            q.push(y);
        }
    }

    ll ans=0;
    for(int i=1;i<=n;i++) ans=(ans+f[i])%mod;

    cout<<ans<<endl;
}

int main() {
    cin.tie(0)->sync_with_stdio(false);
    cout.tie(0);

    int t=1;
    cin>>t;

    while(t--)solve();
}
posted @ 2025-02-28 17:17  宋佳奇  阅读(47)  评论(0)    收藏  举报