Codeforces Round #608 (Div. 2) 題解


Codeforces Round #608 (Div. 2) 題解

前言

題目鏈接:僅僅只是為了方便以題目作為關鍵字能查找到我的題解而已(逃

Codeforces 1271A

Codeforces 1271B

Codeforces 1271C

Codeforces 1271D

Codeforces 1271E

重要:沒有F的題解,想看F題題解的神仙們可以走了

A. Suits

題意

給你\(a\)條領帶,\(b\)條圍巾、\(c\)件背心、\(d\)件夾克,有以下兩種套裝:

  • 一條領帶、一件夾克,花費\(e\)

  • 一條圍巾、一件背心、一件夾克,花費\(f\)

求出最多可以賣多少錢(不能單賣)。(奸商)

做法

依題意可得:

假如有\(i\)\(e\)元的套裝,那么就是價格是\(i \times e + min \{ d-i, b, c \} \times f\)元,枚舉即可(注意 \(i\), \(d-i\), \(b\), \(c\) 中不能出現負數)。

程序

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

int a,b,c,d,e,f,ans;

int main(){

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

    cin>>a>>b>>c>>d>>e>>f;
    for(int i=0;i<=min(a,d);i++){//數據范圍小,直接暴力枚舉
        ans=max(ans,i*e+min((d-i),min(b,c))*f);
    }
    cout<<ans<<endl;

    return 0;
}

B. Blocks

FST的我真的是太菜了

題意

給你一排\(n\)個方塊,每個方塊有黑白兩種顏色,每次操作可以反轉兩個相鄰的方塊的顏色,問能否\(3n\)次數內反轉成一整排都是同一種顏色。

做法

假設序列反轉成白色,那么從前到后跑一遍,如果當前方塊不是白色就反轉它和它后邊的方塊,最后一個方塊如果跑完操作完是白色就OK。黑色同理,假設序列反轉成黑色,那么從前到后跑一遍,如果當前方塊不是黑色就反轉它和它后邊的方塊,最后一個方塊如果跑完操作完是黑色就OK。最后如果都不可以就輸出\(-1\)了。

程序

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

int n;
char s[205];
vector<int> op;

int main(){

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

    cin>>n;
    cin>>s+1;
    //假設全是白色:
    for(int i=1;i<n;i++){
        if(s[i]=='B'){
            s[i]='W';
            s[i+1]=s[i+1]=='W'?'B':'W';
            op.push_back(i);
        }
    }
    //判斷最后一個方塊:
    if(s[n]=='W'){
        cout<<op.size()<<endl;
        for(int i=0;i<op.size();i++){
            cout<<op[i]<<' ';
        }
        cout<<endl;
        return 0;
    }
    //假設全是黑色:(此時可以直接再操作同一個序列)
    for(int i=1;i<n;i++){
        if(s[i]=='W'){
            s[i]='B';
            s[i+1]=s[i+1]=='W'?'B':'W';
            op.push_back(i);
        }
    }
    //判斷最后一個方塊
    if(s[n]=='B'){
        cout<<op.size()<<endl;
        for(int i=0;i<op.size();i++){
            cout<<op[i]<<' ';
        }
        cout<<endl;
        return 0;
    }
    cout<<-1<<endl;

    return 0;
}

C. Shawarma Tent

比B簡單

題意

給你一個平面直角坐標系(剛剛數學課學過誒),之后給你一座學校和\(n\)個學生的坐標(都是整點),問你在一個不同於學校的整點上建一個買Shawarma的帳篷,如果學生從學校到家的某一條神奇路徑上經過就會買(當消費者傻子嗎),問你最多會有幾個學生買,和此時帳篷的坐標(任意一種坐標即可)(奸商)

注:整點指橫坐標、縱坐標都是整數的點。神奇路徑是只能上下左右移動(平行於橫軸或縱軸)的兩點間的最短路徑(長度就是兩點間的曼哈頓距離)(兩點間可能有多條)。

做法

顯然的,距離學校越近,就會有越多學生從帳篷經過,所以只要枚舉學校上下左右的四個點看哪個最優就可以了。

程序

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

int n,sx,sy;
int x[200005],y[200005];
int l,r,u,d;
//l, r, u, d 分別表示左右上下的帳篷經過的學生個數

int main(){

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

    cin>>n>>sx>>sy;
    for(int i=1;i<=n;i++){
        cin>>x[i]>>y[i];
        if(x[i]>sx)r++;
        if(x[i]<sx)l++;
        if(y[i]>sy)u++;
        if(y[i]<sy)d++;
        //對於經過帳篷的學生計數
    }
    if(r>=l&&r>=r&&r>=u&&r>=d){
        cout<<r<<endl;
        cout<<sx+1<<' '<<sy<<endl;
    }else
    if(l>=l&&l>=r&&l>=u&&l>=d){
        cout<<l<<endl;
        cout<<sx-1<<' '<<sy<<endl;
    }else
    if(u>=l&&u>=r&&u>=u&&u>=d){
        cout<<u<<endl;
        cout<<sx<<' '<<sy+1<<endl;
    }else
    if(d>=l&&d>=r&&d>=u&&d>=d){
        cout<<d<<endl;
        cout<<sx<<' '<<sy-1<<endl;
    }
    //這里的u>=u之類只是方便copy&paste罷了

    return 0;
}

D. Portals

題面好長啊,真的不想翻譯

題意

你要攻下\(n\)座城市。一開始你有\(k\)個士兵。攻下第\(i\)個城市,你需要\(a_i\)個士兵(進攻時不損失士兵),攻下后你會抓\(b_i\)個壯丁擴充你的隊伍,攻下后你可以派一個士兵來防守,防守會使得你的分數增加\(c_i\),損失一個士兵。防守有兩種方式:

  • 你可以防守你所在的第\(i\)個城市

  • \(m\)個單向的傳送門,從\(u\)\(v\),如果你在\(u\),可以派兵防守\(v\)(必須是直接被傳送門連接的城市)

你必須從第\(1\)座一直攻克到第\(n\)座城市,如果不能攻克全部城市,你就輸了,輸出\(-1\),如果能贏,求出最大的分數。

做法

貪心的做,計算\(req_i = max \{ a_{i+1} , req_{i+1} - b_{i+1} \} | req_n = 0\)代表在第\(i\)個城市攻克並有\(b_i\)士兵加入后,你完成游戲最少需要的士兵。之后就會有部分士兵空出,第\(i\)個城市空出士兵個數記為\(fr_i\),然后用大根堆來分派士兵到城市就好了。

此時,第\(i\)個城市的可以守衛它的最后一個城市記為\(def_i\),從\(1\)\(i\)的空閑士兵顯然都可以守衛它(空閑士兵可以跟着你到下一個城市)。

程序

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

int n,m,k;
int a[5005],b[5005],c[5005],fr[5005];
vector<int> g[5005];//無用數組
int def[5005],req[5005];
priority_queue<pair<int,int> > pq;
int ans;

int main(){

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

    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i]>>b[i]>>c[i];
        def[i]=i;
    }
    for(int i=1;i<=m;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);//無用語句
        def[v]=max(def[v],u);
    }
    for(int i=n;i>=1;i--){
        req[i]=max(a[i+1],req[i+1]-b[i+1]);cerr<<i<<' '<<req[i]<<endl;
    }
    //計算req[i]
    int cur=k;
    for(int i=1;i<=n;i++){
        if(cur<a[i]){
            cout<<-1<<endl;
            return 0;
        }
        cur+=b[i];
    }
    //↑判斷是否能贏↑
    cur=k;
    for(int i=1;i<=n;i++){
        cur+=b[i];
        fr[i]=cur-req[i];cur=req[i];
        pq.push(make_pair(c[i],i));
    }
    //計算fr[i],初始化以重量為第一關鍵字降序排列的堆
    while(!pq.empty()){
        int val=pq.top().first,x=pq.top().second;
        pq.pop();
        int y=def[x];
        while(!fr[y]&&y>0)y--;
        if(y==0)continue;
        fr[y]--;//貪心地選擇最后一個能選的城市的空閑士兵
        ans+=val;
    }
    cout<<ans<<endl;

    return 0;
}

E. Common Number

題意

做到E的人應該都知道吧。。。而且題面很簡短我就不寫了(逃

做法

定義\(k_i\)為包含\(i\)的路徑的個數。

首先,奇數和偶數分開考慮,你會發現同一個\(n\),同為奇數或偶數時,\(y\)變大了\(k_y\)肯定變小,所以具有單調性,可以二分。

我們再來看對於單個的數,如何計算包含它的路徑條數:

把路徑的計算反過來想,可以得出在路徑的反推中,只有偶數可以加一,而所有數都可以乘二。記錄有多少個可以通過這種方式得到的數就可以了。但是這樣太慢了,所以我們需要一個更快的方法:

通過觀察得出,乘二的次數相同時,從最小可達值(只乘過二)到最大可達值(每次乘二后都加一),之間的所有數都是可以達到的,那么我們只要計算最小值和最大值就好了,復雜度--。由於乘二肯定比加一增長得快,所以對於每一種乘以二的次數,把這個區間的長度加到路徑條數就可以了。

程序

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

typedef long long ll;

ll n,k;

bool check(ll m){
    ll l=m,r=m,res=0;
    if(!(m&1)){r++;}//當m是偶數時給r增加1
    for(int i=1;;i++){//其實i沒有什么用
        res+=min(n,r)-l+1;//添加區間長度到返回值
        //for(int j=l;j<=min(r,n);j++)cerr<<j<<' ';
        l<<=1;
        r=(r<<1)+1;//計算下一個l和r
        if(l>n)break;//當l大於n時就沒有計算的必要了,直接退出
        
    }//cerr<<endl;
    return res>=k;
}

int main(){

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

    cin>>n>>k;
    //for(int i=1;i<=n;i++)check(i);
    ll l=1,r=(n+1)/2,m,ans;//二分奇數
    while(l<=r){
        m=(l+r)>>1;
        if(check(2*m-1)){
            ans=2*m-1;
            l=m+1;
        }else{
            r=m-1;
        }
    }
    l=1;r=n/2;//二分偶數
    while(l<=r){
        m=(l+r)>>1;
        if(check(2*m)){
            ans=max(ans,2*m);//由於ans已經取了奇數的值,所以這里是max
            l=m+1;
        }else{
            r=m-1;
        }
    }
    cout<<ans<<endl;

    return 0;
}

結束語

點個在看吧!

很抱歉不會F題不能幫到部分人,所以我(逃


免責聲明!

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



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