Codeforces 1039E:Summer Oenothera Exhibition(LCT)

本文介绍了一种使用LCT(Link-Cut Tree)优化的算法,该算法通过贪心策略解决了单次询问的问题,并通过优化使得查询效率显著提升。文章详细阐述了如何利用LCT进行查询操作,并给出了具体的时间复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题解:
首先可以贪心 O(n) O ( n ) 解决单次询问。

考虑优化,每个点有一个后继,这个后继在 n ≤ n 的时候,我们暴力修改,否则断开。 查询直接在LCT上查询 O(n) O ( n ) 次,断开的地方用倍增即可。

时间复杂度 O(nnlogn) O ( n n log ⁡ n ) 。当然可以用一些小细节变为 O(n53+n43logn) O ( n 5 3 + n 4 3 log ⁡ n ) ,不过这份LCT的代码比rk2快了接近一倍。

#include <bits/stdc++.h>
using namespace std;
typedef pair <int,int> pii;

const int RLEN=1<<18|1;
inline char nc() {
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
    char ch=nc(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
    return i*f;
}
inline void W(int x) {
    static int buf[50];
    if(!x) {putchar('0'); return;}
    if(x<0) {putchar('-'); x=-x;}
    while(x) {buf[++buf[0]]=x%10; x/=10;}
    while(buf[0]) {putchar(buf[buf[0]--]+'0');}
}

const int N=1e5+50, B=50, L=18;
int n,w,q,ans[N],nxt[N];
int lg[N],x[N],rmq_mx[N][20],rmq_mn[N][20];
vector <pii> det;
vector <pii> ask;

struct node {
    node *fa,*lc,*rc;
    int id,sze;
    node() : fa(NULL),lc(NULL),rc(NULL),sze(0) {}
    inline void upt() {sze=(lc?lc->sze:0)+(rc?rc->sze:0)+1;}
} *pos[N];
inline bool isroot(node *x) {return (!x->fa) || (x->fa->lc!=x && x->fa->rc!=x);}
inline bool which(node *x) {return (x->fa->lc==x);}
inline void rotate(node *x) {
    node *y=x->fa, *z=y->fa;
    if(!isroot(y)) (z->lc==y ? z->lc : z->rc)=x;
    x->fa=z; y->fa=x;
    if(y->lc==x) {
        node *b=x->rc;
        x->rc=y; y->lc=b;
        if(b) b->fa=y;
    } else {
        node *b=x->lc;
        x->lc=y; y->rc=b;
        if(b) b->fa=y; 
    } y->upt(); x->upt();
}
inline void splay(node *x) {
    while(!isroot(x)) {
        node *y=x->fa;
        if(!isroot(y)) {
            if(which(y)==which(x)) rotate(y);
            else rotate(x);
        } rotate(x);
    }
}

inline void access(node *x) {
    for(node *y=NULL;x;y=x,x=x->fa) {
        splay(x); x->rc=y; x->upt();
    }
}
inline void cut(node *x,node *f) {
    access(f); splay(x); x->fa=NULL;    
}
inline void link(node *x,node *f) {
    x->fa=f;
}

inline void modify(int lim) {
    static int p=0;
    while(p<det.size() && det[p].first<=lim) {
        int u=det[p++].second;
        if(nxt[u]+u>n) continue;
        else if((++nxt[u])==B) {
            cut(pos[u],pos[u+nxt[u]]);
        } else {
            cut(pos[u],pos[u+nxt[u]]);
            link(pos[u],pos[u+nxt[u]+1]);
        }
    }
}
inline node* getroot(node *x) {
    while(x->lc) x=x->lc;
    splay(x); return x;
}
inline int find_nxt(int now,int lim) {
    int mx=x[now], mn=x[now];
    for(int i=lg[n-now+1];~i && now<=n;i--) {
        if((now+(1<<i)-1<=n) && max(mx,rmq_mx[now][i])-min(mn,rmq_mn[now][i])<=lim) {
            mx=max(mx,rmq_mx[now][i]);
            mn=min(mn,rmq_mn[now][i]);
            now+=(1<<i);
        }
    } return now;
}
inline int query(int lim) {
    int now=1, cnt=0;
    while(now<=n) {
        access(pos[now]);
        splay(pos[now]);
        cnt+=pos[now]->sze-1;
        now=getroot(pos[now])->id;
        if(now!=n+1) ++cnt, now=find_nxt(now,lim);
    }
    return cnt;
}

int main() {
    n=rd(), w=rd(), q=rd();

    lg[1]=0;
    for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
    for(int i=1;i<=n;i++) rmq_mn[i][0]=rmq_mx[i][0]=x[i]=rd();
    for(int i=1;i<=lg[n];i++) 
        for(int j=1;j+(1<<i)-1<=n;++j)
            rmq_mn[j][i]=min(rmq_mn[j][i-1],rmq_mn[j+(1<<(i-1))][i-1]), 
            rmq_mx[j][i]=max(rmq_mx[j][i-1],rmq_mx[j+(1<<(i-1))][i-1]);

    pos[n+1]=new node();
    pos[n+1]->id=n+1;

    for(int i=n;i>=1;i--) {
        pos[i]=new node();
        pos[i]->fa=pos[i+1];
        pos[i]->sze=1;
        pos[i]->id=i;
    }

    for(int i=1;i<=n;i++) {
        int mx=x[i], mn=x[i];
        for(int j=1;j<=B && i+j<=n;j++) {
            mx=max(mx,x[i+j]);
            mn=min(mn,x[i+j]);
            det.push_back(pii(mx-mn,i));
        }
    }

    sort(det.begin(),det.end());

    for(int i=1;i<=q;i++) {
        int k=w-rd();
        ask.push_back(pii(k,i));
    } 

    sort(ask.begin(),ask.end());

    for(int i=0;i<ask.size();++i) {
        modify(ask[i].first);
        ans[ask[i].second]=query(ask[i].first)-1;
    }

    for(int i=1;i<=q;i++) W(ans[i]), putchar('\n');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值