2018 UESTC Training for Data Structures 一棵像样的线段树

时间:2022-06-12 20:29:16

一棵像样的线段树

设 last[x] 表示使 bi = x 的最大的 i,即 bi 中 x 最后出现的位置.

这样问题就转换为 bi = min last[x]<i−ci{x},也就是满足 last[x] < i−ci 的最小的 x,其中 1 ≤x≤n+ 1,x∈N∗. 可以用线段树维护 last[1..(n+ 1)] 每个区间的最小值. 查询的时候给定一个值 v = i−ci,从最大的区间往下查找, 如果左子区间的最小值大于等于 v,则递归查找右子区间; 否则递归查找左子区间. 当区间长度为 1 时,此区间的左 (右) 端点就是答案,返回之. 得到 bi 后,更新 last[bi] = i,再进行后面的计算. 注意初始时 last[1] = 0,其他 last 值全为小于 0 的值.

以上引用自UESTC 傅大爷 的题解

//389MS   31804KB
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAX=1e6+5;
const int INF=0x3f3f3f3f;
int c[MAX];
int tree[MAX*4];
void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while (s<'0'||s>'9'){if (s=='-')f=-1;s=getchar();}
    while (s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
void update(int r,int L,int R,int pos,int val)
{
    if (L==R)
    {
        tree[r]=val;
        return ;
    }
    int mid=(L+R)>>1;
    if (pos<=mid) update (r*2,L,mid,pos,val);
    else update (r*2+1,mid+1,R,pos,val);
    tree[r]=tree[r*2]>tree[r*2+1]?tree[r*2+1]:tree[r*2];
}
int query(int r,int L,int R,int top)
{
    if (L==R) return L;
    int mid=(L+R)>>1,ans;
    if (tree[r*2]<top) ans=query(r*2,L,mid,top);
    else ans=query(r*2+1,mid+1,R,top);
    return ans;
}
int main ()
{
    int n;
    read(n);
    for (int k=1;k<=n;k++)
        read(c[k]);
    memset(tree,-1,sizeof(tree));
    update(1,1,n+1,1,0);
    for (int k=1;k<=n;k++)
    {
        int x=query(1,1,n+1,k-c[k]);
        update(1,1,n+1,x,k);
        printf ("%d ",x);
    }
    return 0;
}