Codechef Aug2017 #Walks on the binary tree -- 主席树+Hash

传送门

每次答案增加的值就是 n - 之前出现的数与 X LCP 最大值。
而与 X LCP 最大的点在 dfs 序上与 X 的距离最近。而在满二叉树上 X dfs 序上的位置就等于 X
于是可以用 set 维护所有出现过的点,加入 X 时找 X 两边最近的点更新答案。
每个点用主席树+hash维护。

代码
#include<set>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 100010
#define P1 127
#define P2 233
#define M1 1000000007
#define M2 1000000009
struct Hash{
    int s1,s2;
    Hash(int s1=0,int s2=0):s1(s1),s2(s2){}
    bool operator == (Hash a)const{
        return s1==a.s1&&s2==a.s2;
    }
    Hash operator + (Hash a)const{
        return Hash((s1+a.s1)%M1,(s2+a.s2)%M2);
    }
    Hash operator * (int p)const{
        return Hash(s1*p,s2*p);
    }
};
typedef Hash ull;
struct Node{
    int l,r,p,s;
    ull w;
}c[N*75];
int h1[N],h2[N],s1[N],s2[N];
int i,j,k,n,m,p,T,Q,Num,x,Cur;
long long Ans;
char ss[2];
inline void Up(int x){
    c[x].s=c[c[x].l].s+c[c[x].r].s;
    c[x].w=c[c[x].l].w+c[c[x].r].w;
}
inline void Ch(int& x,int y,int p,int l,int r){
    x=++Num;
    c[x].l=c[y].l;c[x].r=c[y].r;
    c[x].p=p;
    c[x].w=Hash((s1[r]-s1[l-1]+M1)%M1,(s2[r]-s2[l-1]+M2)%M2)*p;
    c[x].s=(r-l+1)*p;
}
inline void Down(int x,int l,int r){
    if(c[x].p!=-1){
        int Mid=l+r>>1;
        Ch(c[x].l,c[x].l,c[x].p,l,Mid);Ch(c[x].r,c[x].r,c[x].p,Mid+1,r);
        c[x].p=-1;
    }
}
inline bool Query(int x,int l,int r,int y){
    if(!x)return 0;
    Down(x,l,r);
    if(l==r)return c[x].s;
    int Mid=l+r>>1;
    if(y<=Mid)return Query(c[x].l,l,Mid,y);
    return Query(c[x].r,Mid+1,r,y);
}
inline void Insert(int& x,int y,int l,int r,int L,int R,int p){
    if(l>R||r<L)return;
    Down(x,l,r);
    if(l>=L&&r<=R){
        Ch(x,y,p,l,r);
        return;
    }
    x=++Num;c[x].l=c[y].l;c[x].r=c[y].r;c[x].p=-1;
    int Mid=l+r>>1;
    Insert(c[x].l,c[y].l,l,Mid,L,R,p);
    Insert(c[x].r,c[y].r,Mid+1,r,L,R,p);
    Up(x);
}
inline int Find(int x,int l,int r,int y){
    if(l==r)return c[x].s?-1:l;
    Down(x,l,r);
    int Mid=l+r>>1;
    if(l>=y){
        if(c[c[x].l].s<Mid-l+1)return Find(c[x].l,l,Mid,y);
        return Find(c[x].r,Mid+1,r,y);
    }
    if(y>Mid)return Find(c[x].r,Mid+1,r,y);
    int t=Find(c[x].l,l,Mid,y);
    return t==-1?Find(c[x].r,Mid+1,r,y):t; 
}
inline void Update(int& x,int z){
    bool t=Query(x,1,n,z);
    if(!t)Insert(x,x,1,n,z,z,1);else{
        int p=Find(x,1,n,z);
        if(p==-1)p=n+1;
        Insert(x,x,1,n,z,p-1,0);
        if(p<=n)Insert(x,x,1,n,p,p,1);
    }
}
inline bool Check(int x,int y,int l,int r){
    if(!c[y].s)return 0;
    if(!c[x].s)return 1;
    Down(x,l,r);Down(y,l,r);
    if(l==r)return 0;
    int Mid=l+r>>1;
    if(c[c[x].r].w==c[c[y].r].w)return Check(c[x].l,c[y].l,l,Mid);
    return Check(c[x].r,c[y].r,Mid+1,r);
}
inline int Get(int x,int y,int l,int r){
    if(x)Down(x,l,r);
    if(y)Down(y,l,r);
    if(l==r)return l;
    int Mid=l+r>>1;
    if(c[c[x].r].w==c[c[y].r].w)return Get(c[x].l,c[y].l,l,Mid);
    return Get(c[x].r,c[y].r,Mid+1,r);
}
struct D{
    int x;
    D(int x=0):x(x){}
    bool operator < (D y)const{
        return Check(x,y.x,1,n);
    }
}t;
set<D>S;
set<D>::iterator It;
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&Q);
        Num=Cur=0;Ans=1;S.clear();c[0].p=-1;
        h1[0]=h2[0]=1;
        for(i=1;i<=n;i++)h1[i]=1ll*h1[i-1]*P1%M1,h2[i]=1ll*h2[i-1]*P2%M2,s1[i]=(h1[i]+s1[i-1])%M1,s2[i]=(h2[i]+s2[i-1])%M2;
        while(Q--){
            scanf("%s",ss);
            if(ss[0]=='?')printf("%lld\n",Ans);else{
                scanf("%d",&x);x++;
                Update(Cur,x);
                t=D(Cur);
                if(S.find(t)!=S.end())continue;
                It=S.insert(t).first;
                m=n;
                It++;
                if(It!=S.end())m=Get(Cur,It->x,1,n);
                It--;
                if(It!=S.begin()){
                    It--;
                    m=min(m,Get(Cur,It->x,1,n));
                }
                Ans+=m;
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值