Codeforces 1154E - Two Teams - [线段树+链表]

题目链接:https://codeforces.com/contest/1154/problem/E

 

题意:

$n$ 个人排成一排,第 $i$ 个人的能力值为 $a[i]$,$a[1 \sim n]$ 是 $1 \sim n$ 的某个排列。

第一个教练先来拉人,他会拉目前还在队伍中的 $a[i]$ 最高的那个人,并且把排在它前面和后面的各自 $k$ 个人都拉走,即最多可以拉走 $2k + 1$ 个人。

然后,第二个教练来拉人,也是同样的操作。注意,如果当前被拉走的人的前面或者后面不足 $k$ 个人,那就尽可能多地拉人。

两个教练轮流拉人,直到整个队伍中一个人都不剩。要求你给出最后每个人被哪个教练拉走了。

 

题解:

老年手速选手,时间紧没写完……

一开始先考虑直接用一个线段树搞定,发现有点难……而且时间复杂度也不一定能过。

后来,考虑加入一个链表来维护目前的这一排队伍。

我们用线段树维护这 $n$ 个人的能力值;可以做到对整个区间求最大值,也就能知道目前队伍里哪个人是能力值最大的;其次用线段树做区间修改,可以把被拉走的人的能力值设为 $0$。

然后,对于被拉走的那个人,我们在链表上分别往前、往后移动指针 $k$ 次,就能得到需要删去的那一段链,直接 $O(1)$ 删除。同时在指针移动时顺便把每个人标记好他们是被哪个教练拉走的。

这样一来,时间复杂度为 $O(\frac{n}{k} \log n + n)$。

 

AC代码:

#include
using namespace std;
const int maxn=2e5+10;

int n,k,a[maxn];
int pos[maxn];
int head,tail,pre[maxn],nxt[maxn];
int ans[maxn];

#define ls (rt<<1)
#define rs (rt<<1|1)
struct Node
{
    int l,r;
    int val;
    bool lazy;
    void update()
    {
        val=0;
        lazy=1;
    }
}o[maxn<<2];
void pushdown(int rt)
{
    if(o[rt].lazy)
    {
        o[ls].update();
        o[rs].update();
        o[rt].lazy=0;
    }
}
void pushup(int rt)
{
    o[rt].val=max(o[ls].val,o[rs].val);
}
void build(int rt,int l,int r)
{
    o[rt].l=l, o[rt].r=r;
    o[rt].lazy=0;
    if(l==r)
    {
        o[rt].val=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    pushup(rt);
}
void update(int rt,int st,int ed)
{
    if(st<=o[rt].l && o[rt].r<=ed)
    {
        o[rt].update();
        return;
    }
    pushdown(rt);
    int mid=(o[rt].l+o[rt].r)>>1;
    if(st<=mid) update(ls,st,ed);
    if(mid<ed) update(rs,st,ed);
    pushup(rt);
}
int query(int rt,int st,int ed)
{
    if(st<=o[rt].l && o[rt].r<=ed) return o[rt].val;
    pushdown(rt);
    int mid=(o[rt].l+o[rt].r)>>1;
    int res=0;
    if(st<=mid) res=max(res,query(ls,st,ed));
    if(midmax(res,query(rs,st,ed));
    pushup(rt);
    return res;
}

int main()
{
    cin>>n>>k;
    nxt[head=0]=1, pre[tail=n+1]=n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        pos[a[i]]=i;
        pre[i]=i-1, nxt[i]=i+1;
    }

    build(1,1,n);
    bool team=0;
    while(1)
    {
        int mx=query(1,1,n);
        if(mx<=0) break;

        ans[pos[mx]]=team;
        int l=pos[mx], r=pos[mx];
        for(int i=1;i<=k;i++)
        {
            if(pre[l]==head) break;
            else l=pre[l], ans[l]=team;
        }
        for(int i=1;i<=k;i++)
        {
            if(nxt[r]==tail) break;
            else r=nxt[r], ans[r]=team;
        }
        update(1,l,r);
        int pre_l=pre[l], nxt_r=nxt[r];
        nxt[pre_l]=nxt_r, pre[nxt_r]=pre_l;

        team^=1;
    }

    for(int i=1;i<=n;i++) printf("%d",ans[i]+1);
}

 

你可能感兴趣的:(Codeforces 1154E - Two Teams - [线段树+链表])