smoj2020(平衡树优化dp)

来源:互联网 发布:人工智能在制造业应用 编辑:程序博客网 时间:2024/06/08 19:17

依然是比赛遇到的,我打完暴力就一直在发呆,想到大概要用splay,然而那时只剩15min了,果断弃了(去拿外卖)。

这里写图片描述
对于某天也可以哪里都不去。
这里写图片描述

看起来就像个dp,直接搞个最水的出来。若同样去i个目的地,显然用时越少越优,故设f[i][j]表示前i个,去j个,最后一个目的地最早在哪一天去。

枚举i,j
对于第i个目的地,可以不去,则

f[i][j]=f[i1][j]
只有满足f[i-1][j-1]<r[i]才可以去,有
f[i][j]=max(f[i1][j1]+1,l[i]))
两者取min即为f[i][j]正确信息

这个dp太粗暴了,不好进行更深入的思考,要一步步简化。
首先第一维可以滚动,状态f[i]就表示去i个目的地的最小天数。
显然f数组是严格单调的。

然后把上面转移方程的max改为分类讨论,就变成了这样
① 找到那个满足 f[p]<l[i],f[p+1]≥l[i]的p,将f[p]改为l[i]。
② 对于所有 l≤f[i]≤r 的i,f[i+1]=f[i+1] 。(splay上实现就是区间加,插入和删除结点)

这里f数组的单调性灰常重要,所以才可以一棵splay搞定。

#include <iostream>#include <fstream>#include <algorithm>#include <cmath>#include <ctime>#include <cstdio>#include <cstdlib>#include <cstring>using namespace std;#define mmst(a, b) memset(a, b, sizeof(a))#define mmcp(a, b) memcpy(a, b, sizeof(b))typedef long long LL;const int N=600600,oo=1e9+7;int ans,cnt,n;struct tree{    tree *c[2],*f;    int ad,a;    int d(){return f->c[1]==this;}    void sc(tree *x,int d){(c[d]=x)->f=this;}}nil[N],*root;tree *newtree(int k){    nil->a=nil->ad=0;    nil->c[0]=nil->c[1]=nil->f=nil;    nil[++cnt]=nil[0];    nil[cnt].a=k;    return nil+cnt;}void down(tree *x){    if(x->c[0]!=nil)    {        x->c[0]->a+=x->ad;        x->c[0]->ad+=x->ad;    }    if(x->c[1]!=nil)    {        x->c[1]->a+=x->ad;        x->c[1]->ad+=x->ad;    }    x->ad=0;}void work(tree *x){    if(x->f!=nil)    work(x->f);    down(x);}void zig(tree *x){    tree *y=x->f;    int d=x->d();    y->sc(x->c[!d],d);    if(y->f==nil)    root=x,x->f=nil;    else    y->f->sc(x,y->d());    x->sc(y,!d);}void splay(tree *x){    work(x);    for(tree *y;x->f!=nil;)    {        y=x->f;        if(y->f!=nil)        (x->d() ^ y->d()) ? zig(x) : zig(y);        zig(x);    }}void search(int k){    tree *x,*now=root;    while(now!=nil)    {        if(now->a>=k)        {            x=now;            now=now->c[0];        }        else        now=now->c[1];        down(now);    }    splay(x);}void Cut(tree *x){    while(1)    {        if(x->c[0]!=nil)        {            down(x->c[0]);            zig(x->c[0]);        }        else        if(x->c[1]!=nil)        {            down(x->c[1]);            zig(x->c[1]);        }        else        {            x->f->sc(nil,x->d());            splay(x->f);            break;        }    }}void dfs(tree *x){    down(x);    if(x==nil)    return;    dfs(x->c[0]);    if(x->a>0&&x->a<oo)    ans++;    dfs(x->c[1]);}tree *build(int l,int r){    if(l>r)    return nil;    int mid=(l+r)/2;    tree *hy=newtree(oo);    hy->sc(build(l,mid-1),0);    hy->sc(build(mid+1,r),1);    return hy;}int main(){    cin>>n;    root=newtree(0);    root->sc(build(1,n),1);    for(int i=1;i<=n;i++)    {        int l,r;        scanf("%d%d",&l,&r);        search(l);        root->a++;        root->c[1]->a++;        root->c[1]->ad++;        tree *now=root->c[0];        while(now->c[1]!=nil)        {            down(now);            now=now->c[1];        }        down(now);        now->sc(newtree(l),1);        splay(now->c[1]);        search(r+1);        root->c[1]->a--;        root->c[1]->ad--;        Cut(root);    }       dfs(root);    cout<<ans<<endl;    return 0;}
原创粉丝点击