Codeforces Global Round 5(赛后整理)

Codeforces Global Round 5(赛后补题)

这场的体验挺好的,总体感觉题目给自己一些了good idea,不过就是比赛过程中不争气,只做出来了仅仅三道题目(赛后补题+两道

比赛链接:传送门

A - Balanced Rating Changes

#include
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=2e4+5;
int a[N],b[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,t;
    cin>>n;
    t=0;
    for(int i=1; i<=n; ++i)
    {
        cin>>a[i];
        if(abs(a[i])&1) t++;
        else b[i]=a[i]/2;
    }
    t/=2;
    for(int i=1; i<=n; ++i)
    {
        if((abs(a[i])&1))
        {
            if(t>0)
            {
                b[i]=(a[i]+1)/2;
                t--;
            }
            else
            {
                b[i]=(a[i]-1)/2;
            }
 
        }
    }
    for(int i=1; i<=n; ++i)
        cout<<b[i]<<endl;
}

B - Balanced Tunnel

思路:按照进入的顺序如果比车 a a a 与比该车之前进去的车 b b b 出来的早,那么车a就超车了

#include
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=2e5+5;
int in[N],to[N],out[N],num[N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d",in+i);
        to[in[i]]=i;
    }
    for(int i=1;i<=n;++i){
        scanf("%d",out+i);
        int ps=to[out[i]];
        num[ps]=i;
    }
    int ans=0,maxx=-1;
    for(int i=1;i<=n;++i)
    {;
        if(num[i]<maxx){
            ans++;
        }
        maxx=max(maxx,num[i]);
    }
    cout<<ans<<endl;
} 

C1 - Balanced Removals (Easier)

比赛过程中对于每个点 i i i ,找了与点 i i i体积最小的点 j j j, 然后输出这个点对,如果体积为0,就找曼哈距离最小的点

实际上这题只需找曼哈顿最小距离的点即可,上面的基于体积的策略对曼哈顿距离也同样成立

C2 - Balanced Removals (Harder)

看了T神的题解感觉已经很震撼了,没想到直接递归处理,这个思想也可以扩展到更高维度的解决方案上。

思路

对于 1 1 1维上的点,我们只需将 x x x排序,相邻的成对输出即可

对于 2 2 2维上的点,我们将它们根据 y y y相等进行分类,这样是一个y内有一堆的x,我们对于每个y,将x数组像解决一维上的成对输出即可,如果总个数为奇数,则将这个存起来与下个y剩下的一起输出。

对于 3 3 3维上的点,我们将他们根据 z z z进行分类,这样每个 z z z内都是个二维的信息了,我们按照 2 2 2维那样输出点对即可,如果剩下一下,就留着与下个 z z z的剩下的输出。

时间复杂度为: O ( d ∗ n ∗ l o g n ) O(d*n*logn) O(dnlogn)

#include
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=5e4+5;
int p[N][3];
int n;
int solve(vector<int> ps,int k)//0~2
{
    //从解决第k维度开始的问题,
    if(k==3)
        return ps[0];
    map<int,vector<int> > oo;
    for(int &v:ps){
        oo[p[v][k]].push_back(v);
    }
    vector<int> a;
    for(auto& p :oo)
    {
        int cur=solve(p.second,k+1);
        if(cur!=-1)
            a.push_back(cur);
    }
    for(int i=0;i+1<a.size();i+=2)
        printf("%d %d\n",a[i],a[i+1]);
    if(a.size()%2!=0) return a.back();
    else return -1;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1; i<=n; ++i)
        for(int j=0; j<3; ++j)
            scanf("%d",&p[i][j]);
    vector<int> ps(n);
    iota(ps.begin(),ps.end(),1);
    solve(ps,0);
    return 0;
}

D - Balanced Playlist

首先很容易知道我们将这个环形循环可以拆成线性的,因为更新最大值的原因,所以我们只需复制三次原数组,然后处理,如果对于第 i ∈ [ 1 , n ] i\in[1,n] i[1,n]个开始听的歌曲,可以全部听完后面的所有歌曲,那么这个就会无限循环即-1。实际上如果有一个是无限循环,那么所有的都是无限循环,即数组的最小值小于二倍的数组的最大值。

下面我们考虑将数组复制三次后,对于 i ∈ [ 1 , 3 ∗ n ] i\in[1,3*n] i[1,3n]怎么计算可以听多少歌曲。

对于每个 i i i,coolness 是 a [ i ] a[i] a[i],我们现在要找从这个位置开始,到哪个位置结束。对于 i i i,它的key observe是位置 i i i后面第一个大于 a [ i ] a[i] a[i]的位置 j j j,和位置 i i i 后面第一个小于 a [ i ] 2 \frac{a[i]}{2} 2a[i]的位置k。

T神的题解的key point就是找到这两个关键信息 j , k j,k j,k,然后对 j , k j,k j,k的位置分类讨论得出结果

如果 j < k jj<k,那么 a n s [ i ] = j − i + a n s [ j ] ans[i]=j-i+ans[j] ans[i]=ji+ans[j]

如果 k < j kk<j,那么 a n s [ i ] = k − i ans[i]=k-i ans[i]=ki

问题就迎刃而解了,对于关于每个 i i i j , k j,k j,k的位置我们可以ST表最大最小值+二分线段树+二分单调栈+二分

#include
using namespace std;
const int N=5e4+10;
vector<int> a;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin>>n;
    a.resize(n);
    for(int i=0; i < n; ++i) cin>>a[i];
    int m1,m2;
    m1=*max_element(a.begin(),a.end());
    m2=*min_element(a.begin(),a.end());
    if(m2*2>=m1)
    {
        for(int i=1; i<=n; ++i) cout<<-1<<" ";
        cout<<endl;
    }
    else
    {
        vector<int> o(3*n);
        vector<int> ans(3*n);
        vector<int> st_max,st_min;
        for(int i=0; i<n; ++i)
            o[i]=o[n+i]=o[2*n+i]=a[i];
        int up,down;
        for(int i=3*n-1; i>=0; --i)
        {
            //up   :  o[j]>o[i] 的最小j (j>i)
            //down : 2*o[i] >o[j]的 最小的j
            while(!st_max.empty()&&o[st_max.back()] <= o[i])
                st_max.pop_back();
            while(!st_min.empty()&&o[st_min.back()] >= o[i])
                st_min.pop_back();
 
            if(!st_max.empty()) up=st_max.back();
            else up=-1;
            int th=-1,l=0,r=st_min.size()-1;
            while(l<=r)
            {
                int m=l+r>>1;
                if(2*o[st_min[m]]<o[i]){
                    th=m;
                    l=m+1;
                }
                else r=m-1;
            }
            if(th==-1) down=-1;
            else down=st_min[th];
 
            if(up==-1&&down==-1) ans[i]=3*n-i;
            else if(up==-1) ans[i]=down-i;
            else if(down==-1) ans[i]=up-i+ans[up];
            else if(up<down)
            {
                ans[i]=up-i+ans[up];
            }
            else
                ans[i]=down-i;
            st_max.push_back(i);
            st_min.push_back(i);
        }
        for(int i=0; i<n; ++i ) cout<<ans[i]<<" ";
        cout<<endl;
    }
}

你可能感兴趣的:(100场比赛计划)