M-SOLUTIONS Programming Contest 2020 题解

M-SOLUTIONS Programming Contest 2020 题解

题目质量好高啊,做完感觉好难涨智商了诶。(除了某一道程序又臭又长的F)

A - Kyu in AtCoder

我们可以放很多个if来判断,但这显然不美观。所以,可以观察到,每隔(200)点段位都减少(1),那么就可以直接通过算式算出了。

程序:

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

int main(){

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

    int x;
    cin>>x;
    cout<<10-x/200<<endl;

    return 0;
}

B - Magic 2

不管卡牌颜色,简单来说就是操作后$$A<B<C$$

并不用考虑这(K)次膜法如何分配,反向考虑,最少需要多少次操作使得$$A<B<C$$,这可以贪心的先操作(B)再操作(C)得到。

程序:

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

int main(){

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

    int a,b,c,k,n=0;
    cin>>a>>b>>c>>k;
    while(b<=a)b<<=1,n++;
    while(c<=b)c<<=1,n++;
    cout<<(n<=k?"Yes
":"No
");

    return 0;
}

C - Marks

暴力的计算乘积,肯定要溢出的,所以我们来观察一下:(此处把第(i)天的等第简单地称作(G_i)

[G_i = A_{i-k+1} imes A_{i-k+2} imes dots imes A_{i-1} imes A_i ]

那么,我们可以简单地看出:

[G_i = G_{i-1} imes A_i div A_{i-k} ]

于是只要比较(G_i)(G_{i-1})只需要比较(A_i)(A_{i-k})就好了。程序:

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

int n,k;
int a[200005];

int main(){

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

    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(i>k){
            cout<<(a[i]>a[i-k]?"Yes
":"No
");
        }
    }

    return 0;
}

D - Road to Millionaire

首先,低价买进,高价卖出,这点生意人的素养我们还是要有的。

我们看到,假设第(A)天买入(C)手,第(B)天卖出(C)手,可以转化为:第(A)天买入(C)手,第(A+1)天卖出(C)手,第(A+1)天买入(C)手,第(A+2)天卖出(C)手,……,第(B-1)天买入(C)手,第(B)天卖出(C)手。同时假如这当中有某一天,当天卖出翌日买入是亏损的,那么不买/卖肯定结果更好。

于是,贪心地比较今天和明天的价格并且作出买卖的操作即可获得正确答案。

程序虽然数组叫DP实际上还是贪心:

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

typedef long long ll;

int n,a[85];
ll dp[85];

int main(){

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

    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    dp[1]=1000;
    for(int i=1;i<n;i++){
        dp[i+1]=max(dp[i],dp[i]/a[i]*a[i+1]+dp[i]%a[i]);
    }
    cout<<dp[n]<<endl;

    return 0;
}

E - M's Solution

首先,建立一条铁轨,虽然不一定要穿过居民区,但是,穿过一定能够获得最优结果。

(感性的)证明:假如铁轨不在居民区上,那么两侧的(走向这条铁轨的)居民数量一定相等,否则可以移动向居民数量更大的一边以获得更优结果,而且,假如两侧居民数量相等,那么任意移动铁轨也是可以的。

那么我们枚举要建立的铁轨就好喽。枚举可能的铁轨是(O(3^N))的(对于每一个居民区,都有放东西向铁轨/南北向铁轨/不放铁轨三种做法),计算答案是(O(N^2))的(可以通过二指针来达到(O(N)),但可以卡过去就不用了唷)。题目要求的限定铁轨数量,就在枚举的时候更新对应的答案即可。

程序:(记得考虑初始的两条铁轨)

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

typedef long long ll;

int n,x[15],y[15],p[15],D[15],d[15];
ll ans[16];

int main(){

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

    cin>>n;
    for(int i=0;i<n;i++)cin>>x[i]>>y[i]>>p[i];
    memset(ans,0x3f,sizeof(ans));
    ans[0]=0;
    for(int i=0;i<n;i++){
        D[i]=min(abs(x[i]),abs(y[i]));
        ans[0]+=(ll)p[i]*D[i];
    }
    for(int s=0;s<1<<n;s++){
        for(int t=s;t;t=(t-1)&s){
            memcpy(d,D,sizeof(d));
            for(int i=0;i<n;i++){
                if(s>>i&1){
                    if(t>>i&1){
                        for(int j=0;j<n;j++){
                            d[j]=min(d[j],abs(x[i]-x[j]));
                        }
                    }else{
                        for(int j=0;j<n;j++){
                            d[j]=min(d[j],abs(y[i]-y[j]));
                        }
                    }
                }
            }
            ll cur=0;
            for(int i=0;i<n;i++){
                cur+=(ll)d[i]*p[i];
            }
            ans[__builtin_popcount(s)]=min(ans[__builtin_popcount(s)],cur);
        }
        {
            int t=0;
            memcpy(d,D,sizeof(d));
            for(int i=0;i<n;i++){
                if(s>>i&1){
                    if(t>>i&1){
                        for(int j=0;j<n;j++){
                            d[j]=min(d[j],abs(x[i]-x[j]));
                        }
                    }else{
                        for(int j=0;j<n;j++){
                            d[j]=min(d[j],abs(y[i]-y[j]));
                        }
                    }
                }
            }
            ll cur=0;
            for(int i=0;i<n;i++){
                cur+=(ll)d[i]*p[i];
            }
            ans[__builtin_popcount(s)]=min(ans[__builtin_popcount(s)],cur);
        }
    }
    for(int i=0;i<=n;i++)cout<<ans[i]<<'
';

    return 0;
}

F - Air Safety

这道题考虑不太难,主要是实现恶心人。(AtCoder你变了!)

首先相撞的飞机大体有两种:

  1. 相向飞行的两架飞机,这个对于每一个横/纵坐标单独考虑就好。
  2. 飞行路线相互垂直的两架飞机,假如相撞,由于速度相等,它们到相撞的点的路程一定是相等的。你可以画出一个直角顶点是相撞点,其他两个顶点是飞机初始位置的等腰直角三角形来,就可以发现它们初始位置一定在同一条斜率为(1)(-1)的直线上。那么记录这样的直线所过的向上/下飞行的飞机,再枚举左/右飞行的飞机并查找是否有经过它的直线就好。(先上下还是先左右没什么关系,就是咱喜欢的说)

程序:(还好,不需要离散化)

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

int n,X[200005],Y[200005];
char D[200005];
vector<pair<int,int>> RP,LP;
map<int,set<int>> RL,LL;
vector<int> LR[200005],RR[200005],UC[200005],DC[200005];

int main(){

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

    cin>>n;
    for(int i=0;i<n;i++){
        cin>>X[i]>>Y[i]>>D[i];
        int &x=X[i],&y=Y[i];
        char &d=D[i];
        if(d=='U'){
            UC[x].emplace_back(y);
            LL[y-x].insert(y);
            RL[x+y-1919810].insert(y);
        }
        if(d=='D'){
            DC[x].emplace_back(y);
            LL[x+y-1919810].insert(y);
            RL[y-x].insert(y);
        }
        if(d=='R'){
            RR[y].emplace_back(x);
            RP.emplace_back(x,y);
        }
        if(d=='L'){
            LR[y].emplace_back(x);
            LP.emplace_back(x,y);
        }
    }
    int ans=1e9;
    for(int r=0;r<200000;r++){
        sort(LR[r].begin(),LR[r].end());
        LR[r].emplace_back(1919810);
        sort(RR[r].begin(),RR[r].end());
        for(int &x:RR[r]){
            int y=*lower_bound(LR[r].begin(),LR[r].end(),x);
            if(y!=1919810)ans=min(ans,(y-x)*5);
        }
    }
    for(int c=0;c<200000;c++){
        sort(UC[c].begin(),UC[c].end());
        sort(DC[c].begin(),DC[c].end());
        DC[c].emplace_back(1919810);
        for(int &x:UC[c]){
            int y=*lower_bound(DC[c].begin(),DC[c].end(),x);
            if(y!=1919810)ans=min(ans,(y-x)*5);
        }
    }
    for(pair<int,int> &px:RP){
        int y;
        if(RL.find(px.second-px.first)!=RL.end() && RL[px.second-px.first].lower_bound(px.second)!=RL[px.second-px.first].end()){
            y=*RL[px.second-px.first].lower_bound(px.second);
            ans=min(ans,(y-px.second)*10);
        }
        if(RL.find(px.second+px.first-1919810)!=RL.end() && RL[px.second+px.first-1919810].upper_bound(px.second)!=RL[px.second+px.first-1919810].begin()){
            y=*(--RL[px.second+px.first-1919810].upper_bound(px.second));
            ans=min(ans,(px.second-y)*10);
        }
    }
    for(pair<int,int> &px:LP){
        int y;
        if(LL.find(px.second-px.first)!=LL.end() && LL[px.second-px.first].upper_bound(px.second)!=LL[px.second-px.first].begin()){
            y=*(--LL[px.second-px.first].upper_bound(px.second));
            ans=min(ans,(px.second-y)*10);
        }
        if(LL.find(px.second+px.first-1919810)!=LL.end() && LL[px.second+px.first-1919810].lower_bound(px.second)!=LL[px.second+px.first-1919810].end()){
            y=*LL[px.second+px.first-1919810].lower_bound(px.second);
            ans=min(ans,(y-px.second)*10);
        }
    }
    if(ans==1e9)cout<<"SAFE";
    else cout<<ans;

    return 0;
}
原文地址:https://www.cnblogs.com/BlahDuckling747/p/M-SOLUTIONS_Programming_Contest_2020_Solution.html