[NOI2004 郁闷的出纳员]我的第一个Splay

【算法分析】

以前用SBT做过了,不过为了学习Splay,又用它来做测试了。。。

第一次写Splay,先写了个非指针版的。。。一直WA,后来模范beyondvoid大牛的一些技巧,写了个指针版的才AC。。。

【其它】

居然比SBT快。。。

不过SBT以前用pascal写的,现在是C++,另外我觉得我的SBT使用递归也是一个重要原因吧。

【CODE】

#include const int inf=1000000000;
int limit,ss;
struct Splaytype{
    struct Splaynode{
        Splaynode *l,*r,*fa;
        int key,s;
    };   
   
    Splaynode space[1000000],*root,*p,*q,*null;
    int tot;
    void init(){
        null=&space[0];
        null->l=null;
        null->r=null;
        null->fa=null;
        null->s=0;
        tot=1;
        Splaynode *p=&space[tot];
        p->l=null;
        p->r=null;
        p->fa=null;
        p->key=inf;
        p->s=1;
        root=p;
    }   
   
    inline void update(Splaynode *x){
        if (x==null) return;
        x->s=x->l->s+x->r->s+1;
    }   
   
    void zag(Splaynode *x){
        Splaynode *y=x->fa;
        y->r=x->l;
        x->l=y;
        if (y->fa->l==y) y->fa->l=x;
                    else y->fa->r=x;
        x->fa=y->fa;
        y->fa=x;
        y->r->fa=y;
        update(y);
        update(x);
    }   
   
    void zig(Splaynode *x){
        Splaynode *y=x->fa;
        y->l=x->r;
        x->r=y;
        if (y->fa->l==y) y->fa->l=x;
                    else y->fa->r=x;
        x->fa=y->fa;
        y->fa=x;
        y->l->fa=y;
        update(y);
        update(x);
    }   
   
    void Splay(Splaynode *x,Splaynode *pos){
        if (x==root) return;
        Splaynode *y,*z;
        while (x->fa!=pos){
            y=x->fa; z=y->fa;
            if (z==pos)
              if (y->l==x) zig(x);
                      else zag(x);
            else if (z->l==y)
              if (y->l==x) {zig(y); zig(x);}
                      else {zag(x); zig(x);}
            else
              if (y->r==x) {zag(y); zag(x);}
                      else {zig(x); zag(x);}
        }
    }   
   
    void ins(int key){
        p=root->l,q=root;
        for (;;){
            if (p==null){
                p=&space[++tot];
                p->l=null;
                p->r=null;
                p->s=1;
                p->key=key;
                p->fa=q;
                if (key                           else q->r=p;
                break;
            }else
            if (key                q=p;
                p=p->l;
            }   
            else{
                q=p;
                p=p->r;
            }   
        }
        q=p;
        for (;p!=null;p=p->fa) update(p);
        Splay(q,root);
    }   
   
    void del(){
        p=root->l; q=root;
        for (;;){
            if (p==null){
                Splay(q,root);
                if (q->key+ss                    root->l=q->r;
                    if (q->r!=null) q->r->fa=root;
                    update(root);
                }
                else{
                    q->l=null;
                    update(q);
                    update(root);
                }   
                return;
            }else
            if (p->key+ss>=limit){
                q=p;
                p=p->l;
            }   
            else{
                q=p;
                p=p->r;
            }   
        }   
    }
   
    int get(int rank){
        if (rank>root->s) return -1;
        int done=0,td;
        p=root;
        for (;;){
            if (done+p->r->s+1==rank) return p->key+ss;
            if (done+p->r->s+1>rank) p=p->r;
            else{
                done+=p->r->s+1;
                p=p->l;
            }   
        }   
    }   
}Splay;

int main(){
    freopen("cashier.in","r",stdin);
    freopen("cashier.out","w",stdout);
    Splay.init();
    int n,x,innum=1; char ch;
    scanf("%d%d",&n,&limit);
    for (int i=1;i<=n;i++){
        for (ch=getchar();ch==’ ‘ || ch==’n’;ch=getchar());
        scanf("%d",&x);
        switch (ch){
            case ‘I’:if (x            case ‘A’:ss+=x; Splay.del(); break;
            case ‘S’:ss-=x; Splay.del(); break;
            case ‘F’:printf("%dn",Splay.get(x+1)); break;
        }   
    }   
    printf("%dn",innum-Splay.root->s);
}   

[BSOI 1156]半平面交

【题目大意】

给出N个凸多边形(坐标按逆时针给出),让你求面积交。

【其它】1A

194906 edward_mj 1156 Accepted 184K 15MS G++ 1.73K 2010-02-15 17:53:10

【CODE】

#include #include struct point{double x,y;};
struct line{double a,b,c;};
point p[1000000],pt[1000000],pre,now,st,O;
int n,tot;

void init(){
     O.x=0; O.y=0;
     scanf("%d%d",&tot,&n);
     for (int i=1;i<=n;i++)
         scanf("%lf%lf",&p[i].x,&p[i].y);
}

double X(point p0,point p1,point p2){
       return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}

line getline(point p1,point p2){
     line New;
     New.a=p1.y-p2.y;
     New.b=p2.x-p1.x;
     New.c=X(p1,p2,O);
     return New;
}

point jiaodian(point p1,point p2,point p3,point p4){
      point New;
      line l1=getline(p1,p2),l2=getline(p3,p4);
      double tmp=l1.a*l2.b-l2.a*l1.b;
      New.x=(l1.b*l2.c-l2.b*l1.c)/tmp;
      New.y=-(l1.a*l2.c-l2.a*l1.c)/tmp;
      return New;
}

void cut(){
     int N=0;
     p[n+1]=p[1];
     for (int i=2;i<=n+1;i++)
       if (X(pre,now,p[i-1])<0){
            if (X(pre,now,p[i])>0)
              pt[++N]=jiaodian(pre,now,p[i-1],p[i]);
       }
       else{
            pt[++N]=p[i-1];
            if (X(pre,now,p[i])<0)
              pt[++N]=jiaodian(pre,now,p[i-1],p[i]);
       }
     for (int i=1;i<=N;i++) p[i]=pt[i];
     n=N;
}

void work(){
     int m;
     for (int i=1;i       scanf("%d",&m);
       scanf("%lf%lf",&st.x,&st.y);
       pre=st;
       for (int i=1;i           scanf("%lf%lf",&now.x,&now.y);
           cut();
           pre=now;
       }
       now=st;
       cut();
     }
}

void print(){
     p[n+1]=p[1];
     double ans=0;
     for (int i=1;i<=n;i++)
       ans+=(p[i].x*p[i+1].y-p[i+1].x*p[i].y)/2;
     ans=fabs(ans);
     printf("%.3lfn",ans);
}

int main(){
    init();
    work();
    print();
}

[西南OJ 两个凸多边形交的面积]半平面交

【题目】http://202.115.160.24:8080/JudgeOnline/showproblem?problem_id=1136

【算法分析】

偶模仿http://hi.baidu.com/zyylhappy/blog/item/4ec2410b67e07f2f6a60fb0b.html这个写的。。。

现在基本是理解了。

【其他】1A,第一个半平面交。

143257 edward_mj 1136 Accepted 276K 0MS G++ 1.78K 2010-02-15 17:26:51

【CODE】

#include

[NOI2008 day1 设计路线]树形动态规划


【算法分析】

算是NOI里比较简单的DP

首先应当注意到,只有个3叉以上了分裂点,才会出现不舒适值。所以可以推测不舒适值<=lgN。

于是我们可以枚举不舒适值,进行dp

定义:

f[p][0][ans]为在p点,和儿子没有接轨,最大不舒适度为ans的方案数。

f[p][1][ans]为在p点,和1个儿子有接轨,最大不舒适度为ans的方案数。

f[p][2][ans]为在p点,和2个儿子有接轨,最大不舒适度为ans的方案数。

然后进行树形dp

令T1=f[j][0][ans]+f[j][1][ans]

T2=f[j][0][ans-1]+f[j][1][ans-1]+f[j][2][ans-1]

然后转移:

f[i][2][ans]=f[i][1][ans]*T1+f[i][2][ans]*T2;

f[i][1][ans]=f[i][0][ans]*T1+f[i][1][ans]*T2;

f[i][0][ans]=f[i][0][ans]*T2;

枚举ans,然后判断方案数是否>0,是的输出。

【其它】表示WA了一遍,那个mod=0的bug实在太猥琐了,于是我搞到如果是mod回来=0的话,就=Mod


【CODE】

#include const __int64 N=101111;
struct gtp{__int64 y,next;}g[N*2];
__int64 n,m,Mod,e,ans;
__int64 ls[N],f[N][3][11];

inline void mod(__int64 &x){
    if (x%Mod==0 && x>0) x=Mod;
                    else x=x%Mod;
}   

void dp(__int64 p,__int64 fa){
    f[p][0][ans]=1;
    for (__int64 t=ls[p];t;t=g[t].next)
      if (g[t].y!=fa){
        dp(g[t].y,p);
        __int64 T1,T2;
        T1=(f[g[t].y][0][ans]+f[g[t].y][1][ans]); mod(T1);
        T2=(f[g[t].y][0][ans-1]+f[g[t].y][1][ans-1]+f[g[t].y][2][ans-1]); mod(T2);
        f[p][2][ans]=(f[p][2][ans]*T2+f[p][1][ans]*T1); mod(f[p][2][ans]);
        f[p][1][ans]=(f[p][1][ans]*T2+f[p][0][ans]*T1); mod(f[p][1][ans]);
        f[p][0][ans]=f[p][0][ans]*T2; mod(f[p][0][ans]);
    }   
}   

void work(){
    for (ans=1;;ans++){
        dp(1,0);
        if (f[1][0][ans]+f[1][1][ans]+f[1][2][ans]){
            printf("%dn",ans-1);
            printf("%dn",(f[1][0][ans]+f[1][1][ans]+f[1][2][ans])%Mod);
            return;
        }   
    }   
}   

void add(__int64 x,__int64 y){
    e++;
    g[e].y=y; g[e].next=ls[x]; ls[x]=e;
}   

int main(){
    freopen("design.in","r",stdin);
    freopen("design.out","w",stdout);
    scanf("%I64d%I64d%I64d",&n,&m,&Mod);
    if (m!=n-1){
        printf("-1n");
        printf("-1n");
        return 0;
    }   
    for (__int64 i=1;i<=m;i++){
        __int64 x,y;
        scanf("%I64d%I64d",&x,&y);
        add(x,y);
        add(y,x);
    }   
    work();
}   

[NOI2003 day1 文本编辑器]平衡树合并、二分法构造最平衡的树、个人非主流做法**



【算法分析】

鉴于zyylhappy提到这题,大年初一的凌晨心血来潮爬起来,就把它做了。

网上大部分看到都是块状链表或者说beyondvoid大神的splay。

但是我不会splay,块状链表觉得太不和谐了,于是我果断就拿起了具有我们广东特色的数据结构——陈启峰树(SBT)。虽然说着比较牵强。。。

然后这题嘛,我的思路就是把字符串拆成字符,按位置前后作为key值大小,插入我们可爱的平衡树之中。

虽然我的程序没有定义这个位置,但是这是可以体现出来的。

我们注意到旋转的性质,旋转以后,整体是排序二叉树这一性质不会被破坏(废话

所以我们可以无所顾忌的用SBT去弄到他平衡。

但是我刚写出来的时候一用cena测试。。。悲剧,栈溢出

。。。偶就是为了栈不溢出才学的C++,你现在告诉我你也溢出了

分析一下,我发现我写的时候是找到那个光标位置的下一个插入点的时候,一口气插完了整个字符串,

然后在递归回来修复,这样的话,栈就需要搞到一次插入的字符串长度的大小。而据我不完全统计,它一次插入的字符串有时候甚至长度超10^6(这不是猥琐是什么?)。

偶就改成一个一个字符地插入SBT之中。但是这样的话,插入一个字符串的代价变成O(LEN*lgSUM)。SUM是全部字符的个数。

于是,很果断地TLE,几乎全部跑了6S多。。。(在我这特别残破的机器上,估计是beyondvoid大牛2001买的电脑的1/3的运算速度。。。据beyondvoid大牛说我应该是运行环境有问题,而我悲剧地不会改)

额。然后我想放弃了,但是突然有想到直接插入那颗这么大的平衡树的话,那个lg会变得比较大,而我现在就是被常数卡住了。

于是我就是采取的这样的方法来解决insert操作:先建一颗SBT,然后在找到光标,把新建的SBT的树根拼在那上面。然后这样插入一个长度为LEN的字符串,复杂度会变成O(LEN*lgLEN)

于是高高兴兴地一测,全部4s多,然后我找到时间限制一看,2~4s。于是当场纠结在那。。。我想,如果是beyondvoid的电脑,我是能过的吧

不过我喜欢追求完美。。。所以想搞到在自己这台垃圾机器上也能AC,所以继续优化。

由于上一次注意到他一次插入的字符串长度可能超10^6,于是就觉得肯定是新建SBT的时候O(LEN*lgLEN)的复杂度还是不够快,毕竟他来多几次10^6的话,正常的SBT都要完蛋。。。

于是苦思冥想之后,发现一种新的构建方法——二分区间。

因为是字符串,而且是以先后顺序作为平衡二叉树的关键字,所以我建SBT的话,会每次都找到向右的最长链,然后插入的新的位置当中。如果是有这个单调性的话,那么就可以考虑二分了。

二分构建的话,这棵树会直接达到最平衡,然后插入的复杂度就是O(LEN)!

扯远了。。。先来说说怎么二分。

就是每次把字符串中间的那个字符抽出来放中间,然后左边的放左边去,右边的右边去。

就像这样:

void build(int &p,int l,int r){
    if (l>r) return;
    int mid=(l+r)/2;
    tot++;
    p=tot;
    tr[p].l=0;
    tr[p].r=0;
    tr[p].key=str[mid];
    build(tr[p].l,l,mid-1);
    build(tr[p].r,mid+1,r);
    tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
}   

这样构建以后,一个完全平衡的二叉树插入到SBT某个关键位置的话,旋转次数大大减少。

一测,果然AC!

【其它】经测试,beyondvoid的splay在我这台机器上用了6.22s通过所有的测试数据,我的总共用了5.00s

而我这个长度也算比较短。。。160行,beyondvoid的splay上220行吧。。。块状链表不知。


【CODE】

#include const int N=1025*1025*10;
struct {int l,r,s;char key;}tr[N];
char op[20],str[N];
int tot,root,gb,len,done,nowi,troot;

inline void left(int &k){
    int p=tr[k].r;
    tr[k].r=tr[p].l;
    tr[p].l=k;
    tr[k].s=tr[tr[k].l].s+tr[tr[k].r].s+1;
    tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
    k=p;
}

inline void right(int &k){
    int p=tr[k].l;
    tr[k].l=tr[p].r;
    tr[p].r=k;
    tr[k].s=tr[tr[k].l].s+tr[tr[k].r].s+1;
    tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
    k=p;
}

inline void repair(int &k){
    bool flag=false;
    if (tr[tr[tr[k].l].l].s>tr[tr[k].r].s){
        right(k);
        flag=true;
    }else
    if (tr[tr[tr[k].l].r].s>tr[tr[k].r].s){
        left(tr[k].l);
        right(k);
        flag=true;
    }else
    if (tr[tr[tr[k].r].r].s>tr[tr[k].l].s){
        left(k);
        flag=true;
    }else
    if (tr[tr[tr[k].r].l].s>tr[tr[k].l].s){
        right(tr[k].r);
        left(k);
        flag=true;
    }
    if (flag){
        repair(tr[k].l);
        repair(tr[k].r);
        repair(k);
    }
}

inline void ins(int &p){
    if (p==0) p=troot;
    else
    if (tr[tr[p].l].s+done+1>gb) ins(tr[p].l);
    else{
        done+=tr[tr[p].l].s+1;
        ins(tr[p].r);
    }
    tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
    repair(p);
}

inline char findr(int &p){
    tr[p].s–;
    if (tr[p].r==0){
        char tmp=tr[p].key;
        p=tr[p].l;
        return tmp;
    }
    else return findr(tr[p].r);
}

inline void del(int &p){
     if (p==0 || done>gb+len || done+tr[p].s<=gb) return;
     bool flag=false;
     int td=done+tr[tr[p].l].s+1;
     if (td>gb && td<=gb+len) flag=true;
     if (td-1>gb) del(tr[p].l);
     done=td;
     del(tr[p].r);
     if (flag)
        if (!tr[p].l || !tr[p].r) p=tr[p].l+tr[p].r;
                             else tr[p].key=findr(tr[p].l);
    if (p!=0) tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
}

inline void solve(int p){
    if (p==0 || done>gb+len) return;
    bool flag=false; int td=done;
    if (tr[tr[p].l].s+done+1>gb && tr[tr[p].l].s+done+1<=gb+len) flag=true;
    if (tr[tr[p].l].s+done>gb) solve(tr[p].l);
    if (flag) printf("%c",tr[p].key);
    done=td+tr[tr[p].l].s+1;
    solve(tr[p].r);
}

inline void Move(){scanf("%d",&gb);}
inline void Next(){if (gbinline void Prev(){if (gb>0) gb–;}

inline void build(int &p,int l,int r){
    if (l>r) return;
    int mid=(l+r)/2;
    tot++;
    p=tot;
    tr[p].l=0;
    tr[p].r=0;
    tr[p].key=str[mid];
    build(tr[p].l,l,mid-1);
    build(tr[p].r,mid+1,r);
    tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
}   

inline void Ins(){
    scanf("%d",&len);
    char ch;
    for (int i=0;i        ch=getchar();
        while (ch==’n’) ch=getchar();
        str[i]=ch;
    }
    troot=0;
    build(troot,0,len-1);
    done=0;
    ins(root);
}

inline void Del(){
    scanf("%d",&len);
    done=0;
    del(root);
}

inline void Get(){
    scanf("%d",&len);
    done=0;
    solve(root);
    printf("n");
}

int main(){
    freopen("editor.in","r",stdin);
    freopen("editor.out","w",stdout);
    int tmp;
    scanf("%d",&tmp);
    for (int i=1;i<=tmp;i++){
        scanf("%s",&op);
        switch (op[0]){
            case ‘M’:Move(); break;
            case ‘I’:Ins(); break;
            case ‘D’:Del(); break;
            case ‘N’:Next(); break;
            case ‘P’:Prev(); break;
            case ‘G’:Get(); break;
        }
    }
}