平衡树基础


treap可旋转
搜索,插入和删除操作的期望时间复杂度为Olog(n)
这是一道模板题。

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

插入  数;
删除  数(若有多个相同的数,因只删除一个);
查询  数的排名(若有多个相同的数,因输出最小的排名);
查询排名为  的数;
求  的前趋(前趋定义为小于 ,且最大的数);
求  的后继(后继定义为大于 ,且最小的数)。
输入格式
第一行为 ,表示操作的个数,下面  行每行有两个数  和 , 表示操作的序号()。

输出格式
对于操作 3456 每行输出一个数,表示对应答案。


const int SIZE=100010;
struct Treap
{
    int l,r;
    int val,dat;
    int cnt,size;//cnt是副本数,siz子树中所有节点副本数之和 +自己的副本数 
} a[SIZE];
int tot,root,n,INF=0x7fffffff;
int New(int val)
{
    a[++tot].val=val;
    a[tot].dat=rand();
    a[tot].size=a[tot].cnt=1;
    return tot;
}
void Update(int p)
{
    a[p].size=a[a[p].l].size+a[a[p].r].size+a[p].cnt;
}
void Build()
{
    New(-INF);
    New(INF);
    a[1].r=2;
    root=1;
    Update(root);//这鬼东西有什么用? 
}
int GetRankByVal(int p,int val)//直接统计累加所有比val小的节点数量 
{
    if(p==0)return 0;
    if(val==a[p].val)return  a[a[p].l].size+1;//??加上部分比这个值小的节点数+自己,那把这个+1去掉后面是不是就不用-1了?待会试一试 
    if(val<a[p].val)return  GetRankByVal(a[p].l,val);
     return GetRankByVal(a[p].r,val)+a[a[p].l].size+a[p].cnt;
}
//int  GetValByRank(int p,int rank)//这里吧rank-1,吧后面改成不严格取等对不对?? 
//{
//    if(p==0)return INF;
//    if(a[a[p].l].size>=rank)return GetValByRank(a[p].l,rank);
//    if(a[a[p].l].size+a[p].cnt>=rank)return a[p].val;//不在左二子范围内,只能是这个节点的 
//    return GetValByRank(a[p].r,rank-a[a[p].l].size-a[p].cnt); 
//}
int GetValByRank(int p,int rank)
{
    if(p==0)return INF;
    if(a[a[p].l].size>=rank)return GetValByRank(a[p].l,rank);
    if(a[a[p].l].size+a[p].cnt>=rank)return a[p].val;
    return GetValByRank(a[p].r,rank-a[a[p].l].size-a[p].cnt);
}
void zig(int &p)
{
    int q=a[p].l;
    a[p].l=a[q].r,a[q].r=p,p=q;
    Update(a[p].r);Update(p);
}
void zag(int &p)
{
    int q=a[p].r;
    a[p].r=a[q].l,a[q].l=p,p=q;
    Update(a[p].l);Update(p);
}

void Insert(int &p,int val)
{
    if(p==0)
    {
        p=New(val);
        return; 
    }
    if(val==a[p].val)
    {
        a[p].cnt++,Update(p);
        return;
    }
    if(val<a[p].val)
    {
        Insert(a[p].l,val);
        if(a[p].dat<a[a[p].l].dat)zig(p);//?? 
    }
    else 
    {
        Insert(a[p].r,val);
        if(a[p].dat>a[a[p].r].dat)zag(p); 
    }
    Update(p);
}
int GetPre(int val)
{
    int ans=1,p=root;
    while(p)
    {
        if(a[p].val==val)
        {
            if(a[p].l)
            {
                p=a[p].l;
                while(a[p].r>0)p=a[p].r;
                ans=p;
            }
            break;
        }
        if(a[p].val<val&&a[p].val>a[ans].val)ans=p;
        p=(a[p].val>val)?(a[p].l):a[p].r;
    }
    return a[ans].val;
}
int GetNext(int val)
{
    int ans=2,p=root;//INF
    while(p)
    {
        if(a[p].val==val)
        {
            if(a[p].r>0)
            {
                p=a[p].r;
                while(a[p].l>0)p=a[p].l;
                ans=p;
            }
            break;
        }
        if(a[p].val>val&&a[p].val<a[ans].val)ans=p;
        p=(a[p].val>val)?(a[p].l):(a[p].r);
    }
    return a[ans].val;
}

void Remove(int&p,int val)
{
    if(p==0)return;//没有什么可以删除的 
    if(val==a[p].val)
    {
        if(a[p].cnt>1)
        {
            a[p].cnt--;
            Update(p);
            return;
        }
        if(a[p].l||a[p].r)
        {
            if((a[p].r==0)||(a[p].dat<a[a[p].l].dat))
            {
                zig(p);Remove(a[p].r,val);
            }
            else 
            {
                zag(p);Remove(a[p].l,val);
            }
            Update(p);//旋转之后必更新 
        }
        else p=0;
        return; 
    }
    val<a[p].val?(Remove(a[p].l,val)):(Remove(a[p].r,val));
    Update(p);
    
}
int main()
{
    srand(time(0));
    Build();
    cin>>n;
    while(n--)
    {
        int opt,x;
        opt=re(),x=re();
        if(opt==1)
        {
            Insert(root,x);
        }
        else if(opt==2)
        {
            Remove(root,x);
        }
        else if(opt==3)
        {
            chu("%d\n",GetRankByVal(root,x)-1);//?按大小的排名 
        }
        else if(opt==4)
        {
            chu("%d\n",GetValByRank(root,x+1));//查询排名为x+1的数据和x什么关系? 
        }
        else if(opt==5)
        {
            chu("%d\n",GetPre(x));
        }
        else if(opt==6)
        {
            chu("%d\n",GetNext(x));
        }
    }
    return 0; 
}
treap模板

 

 

 

splay:更加灵活的数据结构
支持:1.区间翻转
    2.其他所有treap支持的简单操作(添加,删除,前驱,后继,排名)

Splay依靠的并不是完全的平衡,根据90-10法则,90%的询问都发生在10%的数据上。


Splay的原理就是:找到询问频率最高的点,把它旋转到根节点,以此在下面的询问中提高效率。


我们认为,我正在访问的点就是询问频率最高的点。


让x成为树根(y):


1.如果y是x的父亲,向上旋x


2.如果x和x的父亲在树上偏的方向相同(左or右孩子),先让x的父亲向上旋,再旋x


3.else让x连续旋两次


//为什么分情况?可以自行画图看一看


//发现按照上述旋转,每次splay以后,整棵树十分的平衡!(接近于完全二叉树)


//如果不分情况,直接无脑上旋,则会结构变得比较乱


 

 

需要注意的点是:

splay的时候,如果说爷爷已经是goal就不再翻了(翻到0就wa了)


#define lson s[x].son[0]
#define rson s[x].son[1]
const int INF=100000089,MAXN=1000007;
struct SPLAY
{
    int f,sub_size,value,tag;
    int son[2];
}s[MAXN];
int original[MAXN],root,wz;
inline bool which(int x)
{
    return x==s[s[x].f].son[1];
}
inline void update(int x)
{
    if(x)
    {
        s[x].sub_size=1;
        if(s[x].son[0])s[x].sub_size+=s[s[x].son[0]].sub_size;
        if(s[x].son[1])s[x].sub_size+=s[s[x].son[1]].sub_size;
    }
}
inline void pushdown(int x)
{
    if(x&&s[x].tag)//x??
    {
        s[x].tag=0;
        swap(s[x].son[1],s[x].son[0]);
        s[lson].tag^=1;
        s[rson].tag^=1;
    }
}
inline void rotate(int x)
{
    int fnow=s[x].f,ffnow=s[fnow].f;
    pushdown(x),pushdown(fnow);
    bool w=which(x);
    s[s[x].son[w^1]].f=fnow;
    s[fnow].son[w]=s[x].son[w^1];
    s[x].son[w^1]=fnow;
    s[fnow].f=x;
    s[x].f=ffnow;
    if(ffnow)
    {
        s[ffnow].son[s[ffnow].son[1]==fnow]=x;
    }
    update(fnow);
    update(x);
}
inline void splay(int x,int goal)//目标指的是让x的父亲是goal 
{
    //chu("dsfdsfdsfdsfdsf\n");
    while(s[x].f!=goal) //只要没到达目标状态 
    {
    //    chu("qqq\n");
        int y=s[x].f,z=s[y].f;
        if(z!=goal)//如果爷爷已经是目标直接把x旋上去,不能再把goal给
        //随便旋转了 
        {
            (s[z].son[1]==y)^(s[y].son[1]==x)?rotate(x):rotate(y);
        }
        rotate(x);
    }
    if(goal==0)root=x;//goal=0就代表着旋转到根节点?? 
}
int build_tree(int l,int r,int fa)
{
    if(l>r)return 0;
    int mid=(l+r)>>1;
    int now=wz+1;
    wz++;
    s[now].f=fa;
    s[now].son[0]=build_tree(l,mid-1,now);
    s[now].son[1]=build_tree(mid+1,r,now);
    s[now].value=original[mid];
    s[now].sub_size=1;
    update(now);
    return now;
}
inline int find(int x){
    int now=root;
    while(1)
    {
        //chu("rrr\n");
        pushdown(now);
        if(x<=s[s[now].son[0]].sub_size){
            now=s[now].son[0];
        //    chu("now:%d\n",now);
        }    
        else  {
        x-=s[s[now].son[0] ].sub_size + 1;
        //chu("changex:%d\n",x);
        if(!x)return now;
        now=s[now].son[1];
        }
    }
}
inline void reverse(int x,int y)
{
    int l=x-1,r=y+1;
    l=find(l);r=find(r);
    //chu("dfsdf\n");
    splay(l,0);
//    chu("dfsdf\n");
    splay(r,l);
//    chu("dfs\n");
    int pos=s[root].son[1];
    pos=s[pos].son[0];
    s[pos].tag^=1; 
}
inline void dfs(int now)
{
    pushdown(now);
    if(s[now].son[0])dfs(s[now].son[0]);
    if(s[now].value!=-INF&&s[now].value!=INF)
    chu("%d ",s[now].value);
    if(s[now].son[1])dfs(s[now].son[1]);
}
int main()
{
//    freopen("makeans.txt","r",stdin);
//    freopen("fire.out","w",stdout);
    int n,m,x,y;
    n=re(),m=re();
    original[1]=-INF;
    original[n+2]=INF;
    _f(i,1,n)original[i+1]=i;
    root=build_tree(1,n+2,0);
    _f(i,1,m)
    {
        x=re(),y=re();
        reverse(x+1,y+1);
    }
    dfs(root);
    return 0;
}
spaly

 

 

 3.给出一段数列,要求查找区间范围内的前驱,后继,一个数排名,排名是几的数

修改位置的值

方法:线段树套平衡树

  1 #include<bits/stdc++.h>
  2 #define rep(i, x, y) for (int i = (x); i <= (y); i ++)
  3 #define down(i, x, y) for (int i = (x); i >= (y); i --)
  4 #define mid ((l+r)/2)
  5 #define lc (o<<1)
  6 #define rc (o<<1|1)
  7 #define pb push_back
  8 #define mp make_pair
  9 #define PII pair<int, int>
 10 #define F first
 11 #define S second
 12 #define B begin()
 13 #define E end()
 14 using namespace std;
 15 typedef long long LL;
 16 //head
 17 
 18 const int N = 4000010;
 19 const int INF = 2147483647;
 20 int n, m, tot, ans, MX;
 21 int a[N], sz[N], cnt[N], ch[N][2], fa[N], data[N], rt[N];
 22 
 23 //=====================================================================
 24 //平衡树
 25 
 26 inline void splayClear(int x)
 27 {
 28     fa[x] = ch[x][0] = ch[x][1] = sz[x] = cnt[x] = data[x] = 0;
 29 }
 30 
 31 inline void pushup(int x)
 32 {
 33     sz[x] = (ch[x][0]?sz[ch[x][0]]:0) + (ch[x][1]?sz[ch[x][1]]:0) + cnt[x];
 34 }
 35 
 36 inline void rot(int x)
 37 {
 38     int y = fa[x], z = fa[y]; bool f = ch[y][1] == x;
 39     ch[y][f] = ch[x][f^1]; if (ch[x][f^1]) fa[ch[x][f^1]] = y;
 40     fa[x] = z; if (z) ch[z][ch[z][1] == y] = x;
 41     fa[y] = x; ch[x][f^1] = y;
 42     pushup(y); pushup(x);
 43 }
 44 
 45 inline void splay(int i, int x, int top)
 46 {
 47     while (fa[x] != top){
 48         int y = fa[x], z = fa[y];
 49         if (z != top) rot((ch[z][0] == y) == (ch[y][0] == x) ? y : x);
 50         rot(x);
 51     }
 52     if (!top) rt[i] = x;
 53 }
 54 
 55 inline void splayInsert(int i, int v)
 56 {
 57     int x = rt[i];
 58     if (!rt[i]){
 59         rt[i] = x = ++ tot;
 60         data[x] = v; sz[x] = cnt[x] = 1;
 61         fa[x] = ch[x][0] = ch[x][1] = 0;
 62         return;
 63     } int last = 0;
 64     while (1){
 65         if (data[x] == v){ cnt[x] ++; pushup(last); break; }
 66         last = x;
 67         x = ch[x][v > data[x]];
 68         if (!x){
 69             x = ++ tot; data[x] = v; sz[x] = cnt[x] = 1;
 70             ch[last][v > data[last]] = x;
 71             fa[x] = last; ch[x][0] = ch[x][1] = 0;
 72             pushup(last); break;
 73         }
 74     }
 75     splay(i, x, 0);
 76 }
 77 
 78 inline int splayRank(int i, int v)//在第i棵splay中求比v小的数的个数
 79 {
 80     int x = rt[i], ret = 0;
 81     while (x){
 82         if (data[x] == v) return ret + ((ch[x][0])?sz[ch[x][0]]:0);
 83         if (data[x] < v){
 84             ret += ((ch[x][0])?sz[ch[x][0]]:0) + cnt[x];
 85             x = ch[x][1];
 86         } else x = ch[x][0];
 87     }
 88     return ret;
 89 }
 90 
 91 inline int splayFind(int i, int v)//在第i棵splay中找到值为v的节点并将它提升到根
 92 {
 93     int x = rt[i];
 94     while (x){
 95         if (data[x] == v){ splay(i, x, 0); return x; }
 96         x = ch[x][v > data[x]];
 97     }
 98 }
 99 
100 inline int splayPre(int i){ int x = ch[rt[i]][0]; while (ch[x][1]) x = ch[x][1]; return x; }
101 inline int splaySuc(int i){ int x = ch[rt[i]][1]; while (ch[x][0]) x = ch[x][0]; return x; }
102 
103 inline void splayDelete(int i, int key)//将第i棵splay的值为key的元素删掉 
104 {
105     int x = splayFind(i, key);
106     if (cnt[x] > 1){ cnt[x] --; pushup(x); return; }
107     if (!ch[x][0] && !ch[x][1]){ splayClear(rt[i]); rt[i] = 0; return; }
108     if (!ch[x][0]){
109         int y = ch[x][1]; rt[i] = y; fa[y] = 0;
110         return;
111     }
112     if (!ch[x][1]){
113         int y = ch[x][0]; rt[i] = y; fa[y] = 0;
114         return;
115     }
116     int p = splayPre(i); int oldrt = rt[i];
117     splay(i, p, 0);
118     ch[p][1] = ch[oldrt][1]; fa[ch[oldrt][1]] = rt[i];
119     splayClear(oldrt);
120     pushup(p);
121 }
122 
123 inline int splayGetpre(int i, int v)
124 {
125     int x = rt[i];
126     while (x){
127         if (data[x] < v){
128             if (ans < data[x]) ans = data[x];
129             x = ch[x][1];
130         } else x = ch[x][0];
131     } return ans;
132 }
133 
134 inline int splayGetsuc(int i, int v)
135 {
136     int x = rt[i];
137     while (x){
138         if (data[x] > v){
139             if (ans > data[x]) ans = data[x];
140             x = ch[x][0];
141         } else x = ch[x][1];
142     } return ans;
143 }
144 
145 //=====================================================================
146 //线段树
147 
148 inline void segInsert(int o, int l, int r, int x, int w)
149 {
150     splayInsert(o, w);
151     if (l == r) return;
152     if (x <= mid) segInsert(lc, l, mid, x, w);
153     else segInsert(rc, mid+1, r, x, w);
154 }
155 
156 //inline void segRank(int o, int l, int r, int x, int y, int v)
157 //{
158 //    if (l == x && r == y){ ans += splayRank(o, v); return; }
159 //    if (y <= mid) segRank(lc, l, mid, x, y, v);
160 //    else if (x > mid) segRank(rc, mid+1, r, x, y, v);
161 //    else segRank(lc, l, mid, x, mid, v), segRank(rc, mid+1, r, mid+1, y, v);
162 //}
163 inline void segRank(int o, int l, int r, int x, int y, int v)
164 {
165     if (x<=l&&r<=y){ ans += splayRank(o, v); return; }
166     if (x <= mid) segRank(lc, l, mid, x, y, v);
167      if (y > mid) segRank(rc, mid+1, r, x, y, v);
168 }
169 
170 inline void segChange(int o, int l, int r, int x, int v)
171 {
172     splayDelete(o, a[x]); splayInsert(o, v);
173     if (l == r){ a[x] = v; return; }
174     if (x <= mid) segChange(lc, l, mid, x, v);
175     else segChange(rc, mid+1, r, x, v);
176 }
177 
178 
179 inline void segPre(int o, int l, int r, int x, int y, int v)
180 {
181     if(x<=l&&r<=y)
182     {
183         ans=max(ans,splayGetpre(o,v));return;
184     }
185     if (x <= mid) segPre(lc, l, mid, x, y, v);
186     if (y > mid) segPre(rc, mid+1, r, x, y, v);
187 }
188 
189 inline void segSuc(int o,int l,int r,int x,int y,int v)
190 {
191     if(x<=l&&r<=y)
192     {
193         ans=min(ans,splayGetsuc(o,v));return;
194     }
195     if(x<=mid)segSuc(lc,l,mid,x,y,v);
196     if(y>mid)segSuc(rc,mid+1,r,x,y,v);
197 }
198 
199 
200 //=====================================================================
201 //第二问
202 
203 inline int getKth(int x, int y, int k)
204 {
205     int ll = 1, rr = MX, mm;
206     while (ll < rr){
207         mm = (ll + rr) / 2;
208         ans = 0; segRank(1, 1, n, x, y, mm);
209         if (ans <=(k-1)) ll = mm+1;
210         else rr = mm ;
211     }
212       return ll-1;
213 }
214 这里的二分很精妙
215 当mm值超过了k-1,mm一定不合法
216 但是我让rr=mm
217 这样在mid二分下,rr会逼近ans,但是永远>ans
218 我让ll去在被勒住的区间不断查找答案,合法时ll=mid+1
219 返回ll-1
220 1 2 2 2  2 4
221 rr在4 的位置,ll是2的话,mid=3222 rr跳到3,mid=2,合法,ll=3,跳出来,2就是合法的
223 恰好是不合法值的上一个是合法的,
224 如果这个值不存在,在l的查找下也会筛出存在的
225 2 2 3 10(query:3)
226 2 2 4 10(query:3)
227 当mid=3时ll会跳到4
228 如果是1,直接不合法跳了
229 如果是2,还是合法会继续
230 mid=4
231 如果是1,rr=4
232 如果是2,ll=5
233 反正怎么都对
234 不能勒左边,因为向下取整
235 int main()
236 {
237     scanf("%d%d", &n, &m);
238     rep(i, 1, n){
239         scanf("%d", &a[i]);
240         segInsert(1, 1, n, i, a[i]);
241         MX = max(MX, a[i]);
242     }
243     while (m --){
244         int opt, x, y, z; scanf("%d%d%d", &opt, &x, &y);
245         if (opt == 1) scanf("%d", &z), ans = 0, segRank(1, 1, n, x, y, z), printf("%d\n", ans+1);
246         else if (opt == 2) scanf("%d", &z), printf("%d\n", getKth(x, y, z));
247         else if (opt == 3) segChange(1, 1, n, x, y),MX=max(MX,y);//??
248         else if (opt == 4) scanf("%d", &z), ans = -INF, segPre(1, 1, n, x, y, z), printf("%d\n", ans);
249         else scanf("%d", &z), ans = INF, segSuc(1, 1, n, x, y, z), printf("%d\n", ans);
250     }
251     return 0;
252 }
253 /*
254 9 6
255 3 2 2 1 9 4 0 1 1
256 2 1 4 3
257 3 4 10
258 2 1 4 3
259 1 2 5 9
260 4 3 9 5
261 5 2 8 5
262 */
树套树

 

 4.应用:

平衡树的本质是在维护一段序列相对大小关系(多半是位置)的前提下,在序列中快速加入或者删除个别元素或区间,并查询相关信息

如果求一段序列,LCQ(i,j)(以此为起点的最大公共前缀个数)

这段序列支持单点修改和修改

用平衡树维护位置大小关系,每个节点代表这颗子树代表的子串

hsh[rt]=hsh[lson]+hsh[rt]*p[sizelson]+hsh[rson]*(p[sizelson+1])

正常的加点和改点,同时动态维护hsh

insert:rt    find(rtsub)  rt根,rtsub右儿子insert(s[rt].son[0])

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<string>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include<iomanip>
#include<bitset>
#include<map>
#include<queue>
#include<bitset>
#include<deque>
#include<vector>
#define _f(i,a,b)  for(register int i=a;i<=b;++i)

#define f_(i,a,b)  for(register int i=a;i>=b;--i)

#define ull unsigned long long

#define chu printf

#define inf 0x7f7f7f7f

using namespace std;
inline int re()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch>'9'||ch<'0')
    {
        if(ch=='-')
        {
            f=-1;
        }
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);ch=getchar();
    }
    return x*f;
}

const int SIZE=1000000;
struct SPLAY
{
    int son[2],fa,size;
    int w;ull hsh;
}s[200000];
int tot,rt;
//29
ull p[150004];//100000
char ss[150000+10];
int len;
inline void Update(int x)
{
    s[x].size=1;
    if(s[x].son[0])s[x].size+=s[s[x].son[0]].size;
    if(s[x].son[1])s[x].size+=s[s[x].son[1]].size;
    s[x].hsh=s[s[x].son[0]].hsh+s[x].w*p[s[s[x].son[0]].size]+s[s[x].son[1]].hsh*p[s[s[x].son[0]].size+1];
    //chu("c=uodate: %d %lld\n",x,s[x].hsh);
}
inline int Build(int l,int r,int f)
{
    if(l>r)return 0;
    int mid=(l+r)>>1;
    s[mid].fa=f;s[mid].w=ss[mid]-'a'+1;
    s[mid].son[0]=Build(l,mid-1,mid);
    s[mid].son[1]=Build(mid+1,r,mid);
    Update(mid);
    return mid;
}
char lin[2];
inline void Rotate(int x)//翻上去x
{
    int fx=s[x].fa,ffx=s[fx].fa;
    bool we=(s[fx].son[1]==x);
    s[s[x].son[we^1]].fa=fx;
    s[fx].son[we]=s[x].son[we^1];
    s[fx].fa=x;
    s[x].son[we^1]=fx;
    s[x].fa=ffx;
    if(ffx)
    {
        s[ffx].son[s[ffx].son[1]==fx]=x;
    }
    Update(fx);Update(x); 
} 
inline void Splay(int x,int goal)
{
    while(s[x].fa!=goal)
    {
        int y=s[x].fa,z=s[y].fa;
        if(z!=goal)
        {
            (s[y].son[1]==x)^(s[z].son[1]==y)?(Rotate(x)):Rotate(y);
        }
        Rotate(x);
    }
    if(goal==0)rt=x;
}
inline int Find(int x,int k)//找到在以x为树根的tree第k个次序的节点,不rotate 
{
    //chu("find:%d  from:%d\n",k,x);
    if(x==0)return 0;
    if(s[s[x].son[0]].size==k-1)return x;
    if(s[s[x].son[0]].size>(k-1))return Find(s[x].son[0],k);
    return Find(s[x].son[1],k-s[s[x].son[0]].size-1);
} 
inline void Insert(int x,int val)//在第x的位置后面加上一个节点
{
    int uio=Find(rt,x+1);
    int iop=Find(rt,x);
    Splay(iop,0);Splay(uio,iop);
    s[uio].son[0]=++tot;
    int bith=s[uio].son[0];
    s[bith].size=1;
    s[bith].w=s[bith].hsh=val;
    s[bith].fa=uio;
    Update(uio);Update(iop);
} 
inline void Change(int x,int val)
{
    int xpos=Find(rt,x);
    Splay(xpos,0);
    s[xpos].w=val;Update(xpos);//这就完了??? 
}
inline ull Gethsh(int x,int y)
{
    //chu("getfrom:%d  to%d\n",x,y);
    int xpos=Find(rt,x-1),xsub=Find(rt,y+1);
//    chu("x:上一位:%d  下一位:%d\n",xpos,xsub);
    Splay(xpos,0);
    Splay(xsub,xpos);
    return s[s[xsub].son[0]].hsh;
}
inline int Make(int x,int y)//找在树里面 
{
    int l=0,r=min(len-x,len-y);//前缀区间长度,不能算我自己加的那个 
    int mid,ans=l;
//    chu("x:%d  y;%d\n",x,y);
    while(l<=r) 
    {
        mid=(l+r)>>1;
    //    chu("mid;%d\n",mid);
        ull xhsh=Gethsh(x,x+mid-1),yhsh=Gethsh(y,y+mid-1);
    //    chu("hsh:%lld %lld\n",xhsh,yhsh);
        if(xhsh==yhsh)
        {
            l=mid+1;ans=mid;
        }
        else r=mid-1;
    }
//chu("x:%d y;%d\n",x,y);
//        chu("%lld %lld\n",Gethsh(x,x),Gethsh(y,y));
    
    return ans;
}
int main()
{
    //freopen("exam.txt","r",stdin);
    p[0]=1;
    _f(i,1,150004)p[i]=p[i-1]*29;//p[i]:29^i
    scanf("%s",ss+2);
//    chu("%s",ss+2);
    len=strlen(ss+2)+2;
    int m=re();//区分有没有?不行,算hsh会出错 
    ss[1]='a'-1,ss[len]='a'-1;//a-1就是0 
    rt=Build(1,len,0);
    //_f(i,1,len)
//    chu("s[%d].w:%d\n",i,s[i].w);
    tot=len;
//    chu("root:%d\n",rt);
    //chu("%d\n",len);
    _f(i,1,m)
    {
        scanf("%s",lin);
        if(lin[0]=='Q')
        {
            int x,y;
            x=re();y=re();
        //    chu("xxx:%d   yyy:%d\n",x,y);
            chu("%d\n",Make(x+1,y+1));
        }
        else if(lin[0]=='R')
        {
            int x=re();scanf("%s",lin);
            Change(x+1,lin[0]-'a'+1);
        }
        else 
        {
            int x=re();scanf("%s",lin);
            Insert(x+1,lin[0]+1-'a');len++;
        }
    }
    return 0; 
}
/*
madamimadam

madamimadam
7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
I 10 a
Q 2 11
第一行给出初始的字符串。第二行是一个非负整数M,
 表示操作的个数。接下来的M行,每行描述一个操作。
 操 作有3种,如下所示 1、询问。语法:Qxy,x,y均为正
 整数。功能:计算LCQ(x,y)限制:1<=x,y<=当前字符串长度。
  2、修改。语法:Rxd,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。
  限制:x不超过当前字 符串长度。 3、插入:语法:Ixd,x是非负整数,d是字符。
  功能:在字符串第x个字符之后插入字符d,如果x=0,则在字 符串开头插入。
  限制:x不超过当前字符串长度
  
splay:
中序遍历位置一定
节点维护:
h[x]节点子树涵盖区间hash值
size[]节点个数
date[]代表单个hash值
 
查询:
二分查找
len:
    l=0,r=nown-(min(x,y))+1
    query(x,x+mid-1),query(y,y+mid-1)
    比较长度
添加一个点
很特殊,在于我不知道明确的可以比较的val,只能根据size维护相对大小
所以insert需要利用二叉树性质 
    find(k)//找到第k个 ,k+1个
    k是rt,k+1是右son,s[k+1].son[0]一定没有 

改变一个点
甩到根节点
直接变data     
更新hash
 
*/
hash+平衡树

 最后找答案就二分区间

 

 

 

5

DP结合线段树:求最长上升子序列

添加的一定是最大的,所以只要找到之前所有历史状态的位置在1--kp之间
dp值最大的转移,那么位置关系的动态灵活维护,就是splay排序判据依赖
位置
那对于DP的转移维护
s[rt].dp代表了rt的整颗子树的dp最大值
s[rt].val则代表从1--pos[rt]的位值转移过来的pos位置dp值
在平衡树动态旋转的过程中,dp会随着update不断变化
但是val只会在某个位置被添加,它被旋到rt,它前面的位置被旋转到左子树的是偶才会更新
因为每次加进去都最大,所以当然只会从比它的位置小的前面的影响(就不用考虑大小了,多好)
继承,那么就这样更新一个val就行
其实因为dp的这个“最大”特殊性质,让它简单了不少

const int SIZE=100000+100;
struct SPLAY
{
    int son[2],f,size;
    int dp,val;
}s[SIZE];
int tot;
int n,rt;
inline void Update(int x)
{
    s[x].size=1;
    s[x].dp=s[x].val;
    if(s[x].son[1])
    {
        s[x].size+=s[s[x].son[1]].size;
        s[x].dp=max(s[x].dp,s[s[x].son[1]].dp);
    }
    if(s[x].son[0])
    {
        s[x].size+=s[s[x].son[0]].size;
        s[x].dp=max(s[x].dp,s[s[x].son[0]].dp);
    }
}
inline void Rotate(int x)
{
    int fx=s[x].f,ffx=s[fx].f;
    bool w=(s[fx].son[1]==x);
    s[s[x].son[w^1]].f=fx;
    s[fx].son[w]=s[x].son[w^1];
    s[fx].f=x;
    s[x].son[w^1]=fx;
    s[x].f=ffx;
    if(ffx)
    {
        s[ffx].son[s[ffx].son[1]==fx]=x;
    }
    Update(fx);Update(x);
}
inline void Splay(int x,int goal)
{
    while(s[x].f!=goal)
    {
        int y=s[x].f,z=s[y].f;
        if(z!=goal)
        {
            (s[y].son[1]==x)^(s[z].son[1]==y)?(Rotate(x)):Rotate(y);
        }
        Rotate(x);
    }
    if(goal==0)rt=x;
}
inline int Find(int pos,int x)
{
//    if(pos==0||x==0)return 0;
    if(x<=s[s[pos].son[0]].size)return Find(s[pos].son[0],x);
    if(s[s[pos].son[0]].size+1==x)return pos;
    return Find(s[pos].son[1],x-s[s[pos].son[0]].size-1);
}
inline void Insert(int x)
{
    int pos=Find(rt,x),sub=Find(rt,x+1);
    Splay(pos,0);Splay(sub,pos);
    s[sub].son[0]=++tot;
    s[tot].f=sub;
    s[tot].size=s[tot].dp=s[tot].val=1;
    Update(sub);Update(pos);
}
int main()
{
    //freopen("exam.txt","r",stdin);
    //freopen("kap.txt","w",stdout);
    n=re();
    rt=1;
    tot=0;
    s[++tot].size=2;
    tot++;
    s[tot-1].son[1]=tot;
    s[tot].size=1;s[tot].f=1;
    int ans=0;
    _f(i,1,n)
    {
        int kp=re()+1;
        Insert(kp);
        Splay(Find(rt,kp+1),0);
        if(s[rt].val<s[s[rt].son[0]].dp+1)s[rt].val=s[s[rt].son[0]].dp+1;
        s[rt].dp=max(s[rt].dp,s[rt].val);
        ans=max(ans,s[rt].val);
        chu("%d\n",ans);
    }
    return 0;
}
DP+splay

 

 

 6给你一棵树,每个点有自己的权值,支持3个操作

(1)Q x:询问从x到根的路径上权值总和

(2)C x y:给x所在子树的每个点权值+y

(3)F x y:让x的父亲变成y

如果没有变换父亲,我们dfs记录每个节点被遍历到的时间戳(两次,一次进去一次出来),进去的权值是点权值,出去的是负点权值,得到一个序列,我们发现一个神奇的性质,从根到x节点的路径之和就是序列date[x.in]的前缀和

因为进去出去的会被抵消,而进去没有出去的说明就在rt--x的路径上,那么就可以用平衡树维护进出序,对于每个节点维护sum,子树的权值和,val,本节点权值,tag懒度标记

对于Q,我们把x的后继旋转到root,那么root的左子树就是答案

对于F,我们把x.in的前驱旋转到root,把x.out的后继旋转到root的右儿子,那么所有会被影响的序列就在后继的左子树里了,直接懒度标记

对于C,我们还是一样的方法把x的子树拎出来,然后直接把x和父亲的关系断掉,再把新的父亲提到root,后继提到rson,后继的lson就直接接上这个子树就行

要注意的就是寻找前驱后继必须先把x提到root

tag标记的更新不仅仅需要把当前节点的下传,还需要把当前节点·到splay的root的全部下传,因为我修改操作由于直接可以通过下标定位,所以相当于有好多在半截修改,这样就需要“一传到底”

const int N=100000+100;
int n,w[N],m,tot,etot,head[N],oula,st[N<<1],st_top=1,sk[N<<1],sk_top,id[N<<1],rt;
struct PAIR
{
    int in,out;
    PAIR(){}
    PAIR(int ini,int outi)
    {
        in=ini,out=outi;
    }
}d[N];//记录入出
struct SPLAY
{
    int fa,in_num,out_num,tag;
    ll val,sum;int son[2];
}t[N<<1];
struct DUG
{
    int to,nxt;
}e[N];
inline void Add(int x,int y)
{
    e[++etot].to=y,e[etot].nxt=head[x],head[x]=etot;
}
inline int Sub(int x)
{
    int now=t[x].son[1];
    while(t[now].son[0])now=t[now].son[0];
    return now;    
}
inline int Pre(int x)
{
    int now=t[x].son[0];
    while(t[now].son[1])now=t[now].son[1];
    return now;
}
inline void Update(int x,ll vl)//把x的值加vl
{
    t[x].val=t[x].val+id[x]*vl;
    t[x].tag+=vl;
    t[x].sum=t[x].sum+vl*(t[x].in_num-t[x].out_num);
}
inline void Pushdown(int x)
{
    if(t[x].tag)
    {
        Update(t[x].son[0],t[x].tag);
        Update(t[x].son[1],t[x].tag);
        t[x].tag=0;
    }
}
inline void Pushup(int x)
{
    t[x].sum=t[t[x].son[0]].sum+t[t[x].son[1]].sum+t[x].val;
    t[x].in_num=t[t[x].son[0]].in_num+t[t[x].son[1]].in_num+(id[x]==1);
    t[x].out_num=t[t[x].son[0]].out_num+t[t[x].son[1]].out_num+(id[x]==-1);
}
inline void Rotate(int x)
{
    int fx=t[x].fa,ffx=t[fx].fa;
    bool op=(t[fx].son[1]==x);
    if(ffx)
    {
        t[ffx].son[t[ffx].son[1]==fx]=x;
    }
    t[x].fa=ffx;
    t[t[x].son[op^1]].fa=fx;
    t[fx].son[op]=t[x].son[op^1];
    t[x].son[op^1]=fx;
    t[fx].fa=x;
    Pushup(fx);Pushup(x);
}
inline void Splay(int x,int goal)
{
    //chu("splay:%d-->%d\n",x,goal);
    int u=x;
    sk[sk_top=1]=u;u=t[u].fa;
    //chu("u:%d\n",t[u].fa);
    while(t[u].fa)sk[++sk_top]=u,u=t[u].fa;
    //chu("sk_tp:%d\n",sk_top);
    f_(i,sk_top,1)Pushdown(sk[sk_top]),sk_top--;//下放标记
    //chu("out\n");
    while(t[x].fa!=goal)
    {
        int fx=t[x].fa,ffx=t[fx].fa;
        if(ffx!=goal)
        {
            ((t[fx].son[1]==x)^(t[ffx].son[1]==fx))?(Rotate(x)):(Rotate(fx));
        }
        Rotate(x);
    }
    if(goal==0)rt=x;
}
inline void Insert(int x,ll vl)
{
    //chu("tot:%d  val:%d\n",tot,vl);
    while(1)
    {
        //chu("dfds\n");
        int xfa=t[x].son[1];
        if(!xfa)
        {
            xfa=++tot;
            t[xfa].fa=x;
            t[x].son[1]=xfa;
            if(id[tot]==-1)t[tot].in_num=1;
            else if(id[tot]==1)t[tot].out_num=1;
            t[tot].val=t[tot].sum=vl;x=xfa;//是这样吧?
            break;
        }
        x=xfa;
    }
    //chu("splay(%d-->%d)\n",x,rt);
    Splay(x,0);//把x转成根
}
inline void dfs(int x)
{
    //chu("递归到:%d(tot:%d)\n",x,tot);
    st[++st_top]=x;d[x].in=st_top;id[st_top]=1;
    Insert(rt,w[x]);
    for(rint i=head[x];i;i=e[i].nxt)
    {
        int to=e[i].to;
        dfs(to);
    }
    st[++st_top]=x;d[x].out=st_top;id[st_top]=-1;
    Insert(rt,-w[x]);
}
inline void Change(int x,int fax)//这是....
{
    Splay(d[x].in,0);
    int a=Pre(d[x].in);
    Splay(d[x].out,0);
    int b=Sub(d[x].out);
    Splay(a,0);
    Splay(b,rt);
    //chu("原来的前驱:%d  后继:%d\n",a,b);
    int goal=t[b].son[0];
    t[b].son[0]=0;
    //chu("goal:%d son:%d\n",goal,t[goal].son[0]);
    Pushup(b);Pushup(rt);
    Splay(d[fax].in,0);
    int uu=Sub(d[fax].in);
    Splay(uu,rt);
    t[uu].son[0]=goal;
    t[goal].fa=uu;
    Pushup(uu);Pushup(rt);
}
inline ll Query(int x)
{
    Splay(d[x].in,0);
    int a=Sub(d[x].in);
    Splay(a,0);
    //chu("root:%d  x:%d\n",rt,x);
    return t[t[a].son[0]].sum;
}
inline void Add_val(int x,ll vl)
{
    Splay(d[x].in,0);
    //chu("din%d  \n",d[x].in);
    int a=Pre(d[x].in);
    Splay(d[x].out,0);
    int b=Sub(d[x].out);
    Splay(a,0);
    Splay(b,a);
    //chu("%d--%d\n",a,b);
    int goal=t[b].son[0];//子树
    t[goal].val+=vl*id[goal];
    //chu("let %d  add:%lld\n",goal,vl*id[goal]);
    t[goal].tag+=vl;
}
char gh[4];
int main()
{
    //freopen("exam.txt","r",stdin);
    //freopen("kap.txt","w",stdout);
    n=re();
    tot=1;
    rt=1;
    //chu("df\n");
    _f(i,2,n)
    {
        int x=re();Add(x,i);
    }
    //chu("thetn:%d\n",tot);
    _f(i,1,n)w[i]=re();
    dfs(1);
    // _f(i,1,5)
    // chu("in;%d(%d)   out:%d(%d)\n",d[i].in,t[d[i].in].val,d[i].out,t[d[i].out].val); 
    // chu("out\n");
    Insert(rt,0);
    m=re();
    _f(i,1,m)
    {
        scanf("%s",gh);
        if(gh[0]=='Q')
        {
            int di=re();
            chu("%lld \n",Query(di));
        }
        else if(gh[0]=='C')
        {
            int xi=re(),yi=re();
            Change(xi,yi);
        }
        else
        {
            int pi=re(),qi=re();
            Add_val(pi,qi);
        }
    }
    return 0;
}
100%

 

posted on 2022-06-04 18:38  HZOI-曹蓉  阅读(53)  评论(0编辑  收藏  举报