Codeforces Global Round 14题解(A~D)

写这篇题解的目的主要是自我反思,最近两天一天打的比一天拉。虽然跟状态确实有一定关系,但我觉得这还反应了更多的问题,反应了我各方面的不足。

总结

虽然姑且比赛时做了四道,但是wa了好多,做的也慢,也维护了那一条负斜率。做题经验确实不足,思维水准也不够高。

题解

A. Phoenix and Gold
A题还是蛮简单的,题目大意是让我们将一个数组重新排序,使得按重新排序后的前缀和没有恰好等于x的,如果不能,则输出"NO",否则输出"YES"以及排序后的数组。
我们只需要贪心的思考即可,分析他给我们的数组,我们直接维护一个当前数组的前缀和,如果遇到一个和为x。我们就需要进行判断,如果当前位存在一个后继,直接交换当前位和后继,这样当前位的前缀和就不在是x,而从下一位开始,前缀和一定大于x (注意题目给定的数组的元素是互不相同的)。如果当前位不存在后继,则数组总和为x,那么我们不论怎么排序,最后的和一定是x。
代码(以下代码可以大大简化因为这里考虑了重复元素,请自行修改,不过我相信你们也不需要)

//#include<bits/stdc++.h> ----万能头----
#include <iostream>
#include<stdio.h>
#include<string>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<cmath>
#include<queue>
#include<list>
#include<map>
#include<unordered_map>
#include<stack>
#include<time.h>
using namespace std;
const int p = 1e9+7;
const int N = 2e4+5;
const int maxn = 1e5+5;
const long long INF = 1e18;
#define REP(i,n) for(ll i = 1; i <= n; ++i)
#define REPA(i,j,n) for(ll i = j; i <= n; ++i)
#define RREP(i,n) for(ll i = n; i >= 1; --i)
#define REPR(i,j,n) for(ll i = n; i >= j; --i)
#define lll __int128
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<double,ll> pdi;
int t;
ll n, m, k;

inline int read(){
    char c=getchar();
    int x=0,f=1;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}

int x[N];
void solve() {
    cin>>n>>m;
    for(int i = 1;  i<=n;++i)cin>>x[i];
    ll sum = 0;
    int pos  = -1;
    for(int i = 1 ; i <= n; ++i){
        sum += x[i];
        if(sum == m){
            pos = i;
            break;
        }
    }
    if(pos == -1){
        cout<<"YES"<<'\n';
        for(int i = 1; i <= n; ++i)cout<<x[i]<<" ";
        cout<<'\n';
        return;
    }
    bool f = 1;
    for(int i = pos + 1; i <= n;++i){//这里昨天考虑了可能重复元素的情况,实际上是没有的
        if(x[i] != x[pos]){
            swap(x[i],x[pos]);
            f = 0;
            break;
        }
    }
    if(f){
        cout<<"NO"<<'\n';
    } else {
        cout<<"YES"<<'\n';
        for(int i = 1; i <= n; ++i)cout<<x[i]<<" ";
        cout<<'\n';
    }


}
int main(){
        //ios::sync_with_stdio(0);
        //cin.tie(0);
        //cout.tie(0);
#ifdef ACM
        freopen("in.txt", "r", stdin);
        freopen("out.txt", "w", stdout);
#endif
        t = 1;
        //srand((unsigned)time(NULL));
        scanf("%lld",&t);
        while (t--)

            solve();

        fclose(stdin);
        fclose(stdout);
}

B. Phoenix and Puzzle
B题呢一开始想的太简单了,虽然所有情况样例确实给出了,但对于边长的情况不知为啥会降智那么多QAQ,导致wa了好多发,后来发现了呢,又因为变量名写错,又wa了几发,导致这题基本没分了(我好菜啊)。
B题是给定我们一个数n,判定是否存在一个正方形由n个等腰直角三角形构成,首先不难想到n必须是偶数,由于我们的大正方形必须是由最小的两个基本正方形构成(即有两个等腰直角或四个等腰直角构成的两个最小正方形),所以这个数必须是2或者4的倍数,那么思考剩下的数会是什么(n/2或者n/4),由于我们要构成正方形,那么边长一定是原本最小正方形的若干倍(假设我们现在将最小正方形边长看成一),可以是两倍,也可以是三倍等等,这样对于每一个边长,我们需要的正方形个数就是边长的平方个。
代码如下

//#include<bits/stdc++.h> ----万能头----
#include <iostream>
#include<stdio.h>
#include<string>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<cmath>
#include<queue>
#include<list>
#include<map>
#include<unordered_map>
#include<stack>
#include<time.h>
using namespace std;
const int p = 1e9+7;
const int N = 2e4+5;
const int maxn = 1e5+5;
const long long INF = 1e18;
#define REP(i,n) for(ll i = 1; i <= n; ++i)
#define REPA(i,j,n) for(ll i = j; i <= n; ++i)
#define RREP(i,n) for(ll i = n; i >= 1; --i)
#define REPR(i,j,n) for(ll i = n; i >= j; --i)
#define lll __int128
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<double,ll> pdi;
int t;
ll n, m, k;

inline int read(){
    char c=getchar();
    int x=0,f=1;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}

int x[N];
void solve() {
    cin>>n;
    if(n & 1){
        cout<<"NO"<<'\n';
        return;
    }
    /*if(n == 4){
        cout<<"YES\n";
        return;
    }*/
    ll tmp = n / 2;
    ll now = sqrt((double)tmp);
    if(now*now == tmp){
        cout<<"YES"<<'\n';
        return;
    }
    if(n % 4 == 0) {
        tmp = n / 4;
        now = sqrt((double) tmp);
        if(now * now == tmp){
            cout<<"YES"<<'\n';
            return;
        }
    }
    cout<<"NO"<<'\n';



}
int main(){
        //ios::sync_with_stdio(0);
        //cin.tie(0);
        //cout.tie(0);
#ifdef ACM
        freopen("in.txt", "r", stdin);
        freopen("out.txt", "w", stdout);
#endif
        t = 1;
        //srand((unsigned)time(NULL));
        scanf("%lld",&t);
        while (t--)

            solve();

        fclose(stdin);
        fclose(stdout);
}


C. Phoenix and Towers

C题更是一言难尽,一开始想复杂了,后来玩了会手机冷静下,就直接过了。。。
C题给了我们n个block(英语水平有限,不敢翻译),m个tower,每个block有一个高度h,这个高度不大于x,要求我们将n个block用尽,且每个tower都至少有一个block,要保证任意两个tower的高度差不超过x,对于这题,我们不能想的太复杂。我们仔细分析一下,注意到每个block高度不大于x,这个条件有什么用呢?我们分析一个已经合法的序列,去最小值mn,最大值mx,mx-mn<x(这里我就懒得用markdown进行美化了),如果我们把任意一个block给最小值,mn = mn + h[i] <mn + x,如果这个数依旧小于mx那没有任何问题,如果大于了mx,mn+h[i]-mx<h[i]<x,也就是说,如果我们每次给当前序列的最小值加上一个block,序列依旧是合法的,所以直接优先队列维护即可。
代码如下

//#include<bits/stdc++.h> ----万能头----
#include <iostream>
#include<stdio.h>
#include<string>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<cmath>
#include<queue>
#include<list>
#include<map>
#include<unordered_map>
#include<stack>
#include<time.h>
using namespace std;
const int p = 1e9+7;
const int N = 2e5+5;
const int maxn = 1e5+5;
const long long INF = 1e18;
#define REP(i,n) for(ll i = 1; i <= n; ++i)
#define REPA(i,j,n) for(ll i = j; i <= n; ++i)
#define RREP(i,n) for(ll i = n; i >= 1; --i)
#define REPR(i,j,n) for(ll i = n; i >= j; --i)
#define lll __int128
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<double,ll> pdi;
int t;
ll n, m, k;
struct node{
    ll h;
    ll id;
    ll pos;
    bool operator < (const node & w){
        return id < w.id;
    }
}e[N];
inline int read(){
    char c=getchar();
    int x=0,f=1;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}
ll x;
void solve() {
    cin>>n>>m>>x;
    for(int i = 1; i <= n; ++i){
        cin>>e[i].h;
        e[i].id = i;
    }
    if(n < m){
        cout<<"NO"<<'\n';
        return;
    }
    priority_queue<PII,vector<PII>,greater<PII>> q;
    for(int i = 1; i <= m; ++i){
        q.push({0,i});
    }
    for(int i = 1; i <= n; ++i){
        ll var = q.top().second;
        ll sum = q.top().first;
        q.pop();
        sum += e[i].h;
        e[i].pos = var;
        q.push({sum,var});
    }
   // sort(e+1,e+1+n);
        cout<<"YES"<<'\n';
        for(int i = 1; i <= n; ++i){
            cout<<e[i].pos<<" ";
        }
        cout<<'\n';
    
}
int main(){
        //ios::sync_with_stdio(0);
        //cin.tie(0);
        //cout.tie(0);
#ifdef ACM
        freopen("in.txt", "r", stdin);
        freopen("out.txt", "w", stdout);
#endif
        t = 1;
        //srand((unsigned)time(NULL));
        scanf("%lld",&t);
        while (t--)

            solve();

        fclose(stdin);
        fclose(stdout);
}

D. Phoenix and Socks
u1s1我觉得D题是前4题最简单的
D题直接贪心就完事了,如果当前有正好配对的socks直接移除,然后去分析剩下的,考虑到left sock的数量和right sock的数量可能不一致,(如果剩下的左右数正好相同,代价就是左(右)边的数量,由于证明很简单,请自行证明),根据对称性,我们只需要分析任意一边数量大的情况,那么我假设左边的sock (直接将sock分成左右两边) 数量大于右边的,我们思考每一个剩下的配对的花费,要么是2,要么就是1,我们贪心的想要获取1,减少2的存在,也就意味着我们需要在左边将适当数量的相同颜色的sock进行配对,这样便是最优的,证明很简单,自行证明,那么问题就解决了。
代码如下

//#include<bits/stdc++.h> ----万能头----
#include <iostream>
#include<stdio.h>
#include<string>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<cmath>
#include<queue>
#include<list>
#include<map>
#include<unordered_map>
#include<stack>
#include<time.h>
using namespace std;
const int p = 1e9+7;
const int N = 2e5+5;
const int maxn = 1e5+5;
const long long INF = 1e18;
#define REP(i,n) for(ll i = 1; i <= n; ++i)
#define REPA(i,j,n) for(ll i = j; i <= n; ++i)
#define RREP(i,n) for(ll i = n; i >= 1; --i)
#define REPR(i,j,n) for(ll i = n; i >= j; --i)
#define lll __int128
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<double,ll> pdi;
int t;
ll n, m, k;
struct node{
    ll h;
    ll id;
    ll pos;
    bool operator < (const node & w){
        return id < w.id;
    }
}e[N];
inline int read(){
    char c=getchar();
    int x=0,f=1;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}
ll l , r;
int lc[N], rc[N];
int c[N];
void solve() {
    cin>>n>>l>>r;
    ll suml = l, sumr = r;
    for(int i = 1; i <= n; ++i)cin>>c[i],lc[i]=0,rc[i] = 0;
    for(int i = 1; i <= l; ++i)lc[c[i]]++;
    for(int i = l + 1; i <= n; ++i){
        rc[c[i]]++;
        if(lc[c[i]]){
            lc[c[i]]--;
            suml--;
            rc[c[i]]--;
            sumr--;
        }
    }
    ll cost = 0;
    if(suml == sumr){
        cout<<suml<<'\n';
        return;
    }
    if(suml < sumr){
        for(int i = l + 1; i <= n; ++i){
            if(rc[c[i]] >= 2){
                int get = rc[c[i]]/2;
                if(sumr - 2*get < suml){
                    cost += (sumr-suml)/2; sumr = suml;
                    break;
                }
                sumr -= 2*get;
                cost += get;
                rc[c[i]] -= 2*get;
            }
        }
        if(suml < sumr){
            ll mid = suml + sumr >> 1;
            cost += sumr - mid;
        }
        cost += ((suml + sumr)>>1);
        cout<<cost<<'\n';
    } else {
        for(int i = 1; i <= l; ++i){
            if(lc[c[i]] >= 2){
                int get = lc[c[i]]/2;
                if(suml - 2*get < sumr){
                    cost += (suml - sumr)/2;
                    suml = sumr;
                    break;
                }
                suml -= 2*get;
                lc[c[i]] -= 2* get;
                cost += get;
            }
        }
        ll mid = (suml + sumr) >> 1;
        if(suml > sumr){
            cost += (suml - mid);
        }
        cost += mid;
        cout<<cost<<'\n';
    }

}
int main(){
        //ios::sync_with_stdio(0);
        //cin.tie(0);
        //cout.tie(0);
#ifdef ACM
        freopen("in.txt", "r", stdin);
        freopen("out.txt", "w", stdout);
#endif
        t = 1;
        //srand((unsigned)time(NULL));
        scanf("%lld",&t);
        while (t--)

            solve();

        fclose(stdin);
        fclose(stdout);
}

由于本人是个菜鸡,写的题解也是菜的一批,请多多谅解QAQ

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值