cf-分球队(访问数组 跳过标记元素 )

题目链接:http://codeforces.com/contest/1154/problem/E
题目大意:有 n学生站成一排。两位教练组成了两支球队 - 第一支教练选择了第一支球队,第二支教练选择了第二支球队。

第 i 学生有整数编程技巧 ai。所有的编程技能在之间1 和 n之间, 包括的1,n。并且每个学生的编程技巧不同。

首先,第一个教练会选择没有考虑到任何一支球队的所有学生中最大的编程技能的学生,和K个离左边最近的学生和K个离右边最近的学生。所有被选中的学生都会离开队伍并加入第一队。其次,第二个教练将采取同样的行动(但他选择的所有学生都加入了第二个团队)。然后第一位教练将再次进行这样的行动,依此类推。这将重复直到该行变空(即,当每个学生成为某个团队时,该过程结束)。

你的问题是确定哪些学生将被带入第一队,哪些学生将被带入第二队。

在这里插入图片描述

思路:主要解决,每次访问当前最大值的位置,和他左右的k个人。如果只用数组模拟。O(n^2)复杂度。因为重复访问了已经标记的位置。如果用链表,每此删除后。查找当前最大值的位置是O(n)。实际上没有O(n),因为有的元素删除了。如果k=1,那么类似于O(n)。

我们可以用L[i]表示第i个位置的左边第一个没有标记的坐标,R[i]表示第i个位置的右边第一个没有标记的坐标。

每次标记后维护就行。
在这里插入图片描述
如果第1次最大值元素的位置在4处。k=2,那么下次的L[], R[]如图。
下次如果访问到1时,R[1]直接跳过了2, 3, 4, 5, 6。到7。从7开始L[7]=1。

问:如果访问到2到7中间的元素怎么办,答:不可能,因为2-7已经标记,不可能再访问到。

因为最大值开始为n,然后为n-1,一直到1,判断该位置是否访问就行了。

#include<bits/stdc++.h>
#define LL long long
//#define p1 first
//#define p2 second
using namespace std;

//memset(a, 0, sizeof(a));
//stack堆栈 queue队列 priority_queue优先队列
//vector向量 multiset平衡二叉树 deque双端队列
//pair{T1 first;T2 second;} greater<T>
//unordered_map 哈希map
int vis[200005]={0};
int w[200005]={0};
int L[200005], R[200005];
int a[200005];
int main()
{
    int n, k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        w[a[i]]=i;
        L[i]=i-1, R[i]=i+1;
    }
    int MAX=n, p, f=1;
    while(1)
    {
        p=k;
        vis[w[MAX]]=f%2?1:2;
        int l=w[MAX], r=w[MAX];
        for(int i=L[w[MAX]];i>=0&&p>=0;i=L[i])
        {
            l=i;
            if(i==0||p==0)
            {
                break;
            }
            if(!vis[i])
            {
                vis[i]=f%2?1:2;
                p--;
            }
        }
        p=k;
        for(int i=R[w[MAX]];i<=n+1&&p>=0;i=R[i])
        {
            r=i;
            if(i==n+1||p==0)
            {
                break;
            }
            if(!vis[i])
            {
                vis[i]=f%2?1:2;
                p--;
            }
        }
        f++;
        R[l]=r, L[r]=l;
        MAX--;
        while(vis[w[MAX]]&&MAX)
        {
            MAX--;
        }
        if(!MAX)
        {
            break;
        }
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d",vis[i]);
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值