Codeforces Round #617 (Div. 3) 題解


Codeforces Round #617 (Div. 3) 題解

前言

這次比賽的題目都好有意思啊。STL真好用

包含題目:
CF 1296ACF 1296BCF 1296CCF 1296DCF 1296E1CF 1296E2CF 1296F

A. Array with Odd Sum

題意

給你一個序列,其中可以把任意一個的元素的值賦給任意一個另外的元素,這個操作可以進行任意次,也可不進行。問你能否把它們的和變成一個奇數。

做法

我們先計算奇數和偶數的個數,之后分類討論:

  • 全是單數
    • 如果\(n\)是偶數,就輸出NO
    • 如果\(n\)是奇數,就輸出YES
  • 在有至少一個偶數的情況下
    • 如果沒有奇數,輸出NO
    • 如果有至少一個奇數,輸出YES

程序

其中n&1可以簡單的認為等價於n%2但是運算優先級不一樣

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

int t,n,a[2005];

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>t;
    while(t--){
        cin>>n;
        int odd=0,even=0;
        for(int i=0;i<n;i++){
            cin>>a[i];
            if(a[i]&1){
                odd++;
            }else{
                even++;
            }
        }
        if(even==0){
            if(n&1){
                cout<<"YES"<<endl;
            }else{
                cout<<"NO"<<endl;
            }
        }else{
            if(odd==0){
                cout<<"NO"<<endl;
            }else{
                cout<<"YES"<<endl;
            }
        }
    }

    return 0;
}

B. Food Buying

題意

給你一個數\(s\)為初始錢數,你每次花\(x\)元,花完后可以返還\(\lfloor \frac x {10} \rfloor\)元(\(x\)除以\(10\)下取整)。問你最多能花出去多少錢。

做法

每次把個位數上的零頭留着,花掉十位數往上的整錢,把返還的錢數和當前的錢數合並,再繼續花下去。直到你只有零錢了,就全部花掉。

程序

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

int t,n,ans;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>t;
    while(t--){
        cin>>n;
        ans=0;
        while(n>=10){
            ans+=n-n%10;
            n=n/10+n%10;
        }
        cout<<ans+n<<endl;
    }

    return 0;
}

C. Yet Another Walking Robot

題意

給你一個機器人,每一步可以上下左右走,再給一個機器人的移動步驟的字符串,讓你刪除一個非空子串(連續的),使得機器人停止的位置和沒有刪除之前停止的位置相同。

做法

前綴和,得到執行了每一步所停在的位置,如果有兩個不同的字符串中的位置使得機器人停在的位置相同,那么可以刪去第一個字符串中的位置往后一個到第二個字符串位置的子串。特別的,機器人未開始時在的位置也要計算進去。

程序

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

int t,n;
string s;
int px;
int py;
map<pair<int,int>,set<int> > mp;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>t;
    while(t--){
        cin>>n;
        cin>>s;
        px=py=0;
        mp.clear();
        mp[make_pair(0,0)].insert(-1);
        for(int i=0;i<n;i++){
            px=px+(s[i]=='R')-(s[i]=='L');
            py=py+(s[i]=='U')-(s[i]=='D');
            mp[make_pair(px,py)].insert(i);
        }
        int ansl,ansr,ans=1e9;
        for(map<pair<int,int>,set<int> >::iterator it=mp.begin();it!=mp.end();it++){
            if(it->second.size()>1){
                vector<int> v;
                for(set<int>::iterator i=it->second.begin();i!=it->second.end();i++){
                    v.push_back(*i);
                }
                for(int l=0,r=0;l<v.size();l++){
                    while(r<v.size()&&v[r]-v[l]<=0)r++;
                    if(r==v.size())break;
                    if(v[r]-v[l]<ans){
                        ans=v[r]-v[l];
                        ansl=v[l]+2;
                        ansr=v[r]+1;
                    }
                }
            }
        }
        if(ans==1e9){
            cout<<-1<<endl;
        }else{
            cout<<ansl<<' '<<ansr<<endl;
        }
    }

    return 0;
}

D. Fight with Monsters

題意

\(n\)個怪物,每個血量\(h_i\),你攻擊力\(a\),你對手攻擊力\(b\),怪物血量小於等於\(0\)時死亡。遇到怪物時你先手,你對手后手,輪流攻擊,造成最后一擊的人拿到分數。你可以跳過你對手的回合\(k\)次,使他不能攻擊。你們從第一個怪物打到第\(n\)個,問你能拿到的最大的分數。

做法

對於每個怪物,我們計算你造成最后一擊所需要的跳過對手回合的次數:

  • 首先把怪物的血量對\(a+b\)取模,之后如果是\(0\),那么再加上\(a+b\),保證不跳過的話,你和對手攻擊過后怪物必定死亡。
  • 然后計算你要打\(c_i\)次殺死怪物,那么顯然需要跳過你的對手\(c_i-1\)次。

\(c_i-1\)的數組排個序從小到大貪心的使用\(k\)次機會即可。

程序

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

typedef long long ll;

ll n,a,b,k;
ll h[200005];

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>n>>a>>b>>k;
    for(ll i=0;i<n;i++){
        cin>>h[i];
        h[i]%=a+b;
        if(h[i]==0)h[i]+=a+b;
        h[i]=(h[i]-1)/a;
    }
    sort(h,h+n);
    ll ans=0;
    for(int i=0;i<n;i++){
        k-=h[i];
        if(k>=0)ans++;
        else break;
    }
    cout<<ans<<endl;

    return 0;
}

E1. String Coloring (easy version)

題意

給你一個字符串,讓你把它塗成兩種顏色,可以交換相鄰的不同顏色的元素,問你能不能把它搞成有序的。

做法

顯然交換相鄰是冒泡排序的思想。可以看出,消除逆序對,需要它們的顏色不同,於是,我們可以分別維護顏色為\(0\)\(1\)的目前元素的最大值,從前到后的處理字符串,貪心的先塗成\(0\),假如\(0\)中元素的最大值比它大,那么就會造成不能排序,此時就塗成\(1\),假如還是有最大值比它大,那么就輸出-1,否則就更新當前顏色的最大值,就可以了。

程序

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

int n;
string s;
int col[205];
int cnt,mx[2];

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>n;
    cin>>s;
    for(int i=0;i<n;i++){
        if(mx[0]>s[i]){
            if(mx[1]>s[i]){
                cout<<"NO"<<endl;
                return 0;
            }else{
                mx[1]=s[i];
                col[i]=1;
            }
        }else{
            mx[0]=s[i];
            col[i]=0;
        }
    }
    cout<<"YES"<<endl;
    for(int i=0;i<n;i++)cout<<col[i];
    cout<<endl;

    return 0;
}

E2. String Coloring (hard version)

題意

請照着E1理解。現在你可以塗無限多種顏色,求搞成有序序列最少要塗多少種不同的顏色。

做法

基本想法還是相同的。消除逆序對需要兩個端點是不同的顏色,於是我們就記錄每一種顏色對應的最大值,保存為數組。我們把保存顏色\(i\)中最大的元素的數組叫作\(mx_i\),對於每一位\(s_i\)的更新,我們需要快速的查詢這個數組中第一個比\(s_i\)小或者相同的元素位置。由於每一次更新第\(i\)位時,保證\(mx\)前面的元素都比\(i\)大,所以這個數組單調遞減,可以二分地找到這個位置。

程序

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

int n,sz,ans;
char s[200005];
int col[200005];
int mx[200005];

int qry(int val){
    int l=1,r=n,m,res;
    while(l<=r){
        m=(l+r)>>1;
        if(mx[m]<=val){
            res=m;
            r=m-1;
        }else{
            l=m+1;
        }
    }
    return res;
}

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>n;
    sz=1;while(sz<n)sz<<=1;
    cin>>s+1;
    for(int i=1;i<=n;i++){
        int pos=qry(s[i]);
        mx[pos]=s[i];
        col[i]=pos;
        ans=max(ans,pos);
    }
    cout<<ans<<endl;
    for(int i=1;i<=n;i++)cout<<col[i]<<' ';
    cout<<endl;

    return 0;
}

F. Berland Beauty

題意

給你一棵樹,每條邊有一個權值,告訴你數個條件,每個條件告訴你某兩點之間的簡單路徑上的所有的邊中權值最小的一條邊的權值,問你能否構建出這樣的滿足所有條件的樹,並輸出可行方案。

做法

由於數據范圍十分的小,所以暴力就好了。

我們首先規定\(1\)是這棵樹的根,然后預處理每個節點的深度。然后,兩個節點\(u,v\)間的簡單路徑,必定是\(u\)\(lca(u,v)\)\(v\)\(lca(u,v)\)的這段路程的和,此處\(lca(u,v)\)\(u,v\)的最近公共祖先(有根樹意義下)。於是簡單路徑便可以這么求得:把較深的節點通過不斷的變換為它的父親的方式提上來,之后同時提兩個節點,直到變成同一個。經過的邊就是它們之間的簡單路徑。

對於每一條邊,為了滿足條件,它的權值必定是所有影響它的條件中的權值的最大值,我們直接把它的權值就固定了,假如沒有影響它的邊,我們就賦值為任意的值。比如114514

最后我們在查看一遍每個條件是否滿足了就好了。

程序

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

int n,m;
vector<pair<int,int> > g[5005];
pair<pair<int,int>,int> q[5005];
int d[5005];
pair<int,int> par[5005];
int f[5005];

void dfs(int x,int p,int cd){
    d[x]=cd;
    for(int i=0;i<g[x].size();i++){
        int &y=g[x][i].first,&z=g[x][i].second;
        if(y!=p){
            dfs(y,x,cd+1);
            par[y]=make_pair(x,z);
        }
    }
}

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>n;
    for(int i=1;i<n;i++){
        int a,b;
        cin>>a>>b;
        g[a].push_back(make_pair(b,i));
        g[b].push_back(make_pair(a,i));
        f[i]=1;
    }
    dfs(1,-1,1);
    cin>>m;
    for(int i=1;i<=m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        q[i]=make_pair(make_pair(a,b),c);
        if(d[a]>d[b])swap(a,b);
        while(d[b]>d[a]){
            f[par[b].second]=max(f[par[b].second],c);
            b=par[b].first;
        }
        while(b!=a){
            f[par[a].second]=max(f[par[a].second],c);
            a=par[a].first;
            f[par[b].second]=max(f[par[b].second],c);
            b=par[b].first;
        }
    }
    for(int i=1;i<=m;i++){
        bool fl=false;
        int a,b,c;
        a=q[i].first.first;
        b=q[i].first.second;
        c=q[i].second;
        if(d[a]>d[b])swap(a,b);
        while(d[b]>d[a]){
            if(f[par[b].second]==c)fl=true;
            b=par[b].first;
        }
        while(b!=a){
            if(f[par[a].second]==c)fl=true;
            a=par[a].first;
            if(f[par[b].second]==c)fl=true;
            b=par[b].first;
        }
        if(!fl){
            cout<<-1<<endl;
            return 0;
        }
    }
    for(int i=1;i<n;i++){
        cout<<f[i]<<' ';
    }
    cout<<endl;

    return 0;
}

謝謝觀看

emmm,覺得E題的貪心寫得很爛,主要賽時突然就想到了,感性理解一波就直接交了,有什么不懂的可以問啦。

會有人看&問嗎


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM