题目描述

  有一棵树,每条边上面都有一个字母。每个点还有一个特征值\(a_i\)。

  定义一个节点\(i\)对应的字符串为从这个点到根的路径上所有边按顺序拼接而成的字符串\(s_i\)。

  有\(m\)次操作:

  • \(0~u~l~r\):询问有多少个字符串\(s_i\)满足\(lcp(s_i,s_u)\geq l\)且\(a_i\leq r\)
  • \(1~f~a~c\):新增一个点,父亲为\(f\),到父亲的边上的字符为\(c\)。

  强制在线。

  \(n,m\leq 40000\)

题解

  因为要加新的点而且要强制在线,所以只能用后缀平衡树了。

  然后就会发现这是一道非常水的题了。

  维护后缀平衡树,每个点再维护关键字为这个区间内每个点的特征值的一棵平衡树,也就是树套树。

  后缀平衡树可以做到\(O(1)\)比较大小,但是这题不需要。

  (其实\(O(1)\)求上面那个东西会跑的很快,但是我很懒。)

  时间复杂度:\(O((n+m)\log^2 (n+m))\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<utility>
#include<iostream>
#include<ctime>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
int rd()
{
int s=0,c;
while((c=getchar())<'0'||c>'9');
s=c-'0';
while((c=getchar())>='0'&&c<='9')
s=s*10+c-'0';
return s;
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
char str[100];
sprintf(str,"%s.in",s);
freopen(str,"r",stdin);
sprintf(str,"%s.out",s);
freopen(str,"w",stdout);
#endif
}
int id(char c)
{
switch (c)
{
case 'A':return 1;
case 'C':return 2;
case 'G':return 3;
case 'T':return 4;
}
return 0;
}
namespace mempool
{
int a[2000010];
int t;
void init()
{
t=2000000;
for(int i=1;i<=t;i++)
a[i]=i;
}
int get()
{
return a[t--];
}
void push(int x)
{
a[++t]=x;
}
}
const ll inf=0x7fffffffffffffffll;
const ull base=127;
ull pw[20];
int n,m;
int a[100010];
//ll hs[100010];
int f[100010][18];
int d[100010];
ull h[100010][18];
int lcp(int x,int y)
{
if(d[x]>d[y])
swap(x,y);
int s=0;
for(int i=16;i>=0;i--)
if((1<<i)<=d[x])
if(h[x][i]==h[y][i])
{
x=f[x][i];
y=f[y][i];
s+=1<<i;
}
return s;
}
int cmp(int x,int y)
{
for(int i=16;i>=0;i--)
if((1<<i)<=d[x]&&(1<<i)<=d[y])
if(h[x][i]==h[y][i])
{
x=f[x][i];
y=f[y][i];
}
return (h[x][0]!=h[y][0]?(h[x][0]<h[y][0]?1:-1):0);
}
namespace treap
{
struct node
{
int k;
int c[2],s;
int v;
int f;
int l,r;
};
node a[2000010];
int newnode()
{
int p=mempool::get();
a[p].k=rand();
a[p].v=a[p].c[0]=a[p].c[1]=a[p].s=a[p].f=a[p].l=a[p].r=0;
return p;
}
void mt(int p)
{
a[p].s=a[a[p].c[0]].s+a[a[p].c[1]].s+1;
a[p].r=(a[p].c[1]?a[a[p].c[1]].r:a[p].v);
a[p].l=(a[p].c[0]?a[a[p].c[0]].l:a[p].v);
}
int ins(int &p,int x,int f=0)
{
if(!p)
{
p=newnode();
a[p].v=a[p].l=a[p].r=x;
a[p].s=1;
a[p].f=f;
return p;
}
int y;
if(x<=a[p].v)
y=ins(a[p].c[0],x);
else
y=ins(a[p].c[1],x);
mt(p);
return y;
}
void rotate(int x)
{
int p=a[x].f;
int q=a[p].f;
int ps=(x==a[p].c[1]);
int qs=(p==a[q].c[1]);
int c=a[x].c[ps^1];
if(a[p].f)
a[q].c[qs]=x;
a[x].c[ps^1]=p;
a[p].c[ps]=c;
if(c)
a[c].f=p;
a[p].f=x;
a[x].f=q;
mt(p);
mt(x);
}
void insert(int &p,int x)
{
int y=ins(p,x);
while(a[y].f&&a[a[y].f].k>a[y].k)
rotate(y);
while(a[p].f)
p=a[p].f;
}
void del(int &p)
{
if(a[p].c[0])
del(a[p].c[0]);
if(a[p].c[1])
del(a[p].c[1]);
mempool::push(p);
p=0;
}
int query(int p,int v)
{
if(!p)
return 0;
if(a[p].l>v)
return 0;
if(a[p].r<=v)
return a[p].s;
if(a[p].v<=v)
return a[a[p].c[0]].s+1+query(a[p].c[1],v);
return query(a[p].c[0],v);
}
void dfs(int p,int f)
{
a[p].f=f;
if(a[p].c[0])
dfs(a[p].c[0],p);
if(a[p].c[1])
dfs(a[p].c[1],p);
mt(p);
}
int st[100010];
void build(int &p,int *e,int l,int r)
{
int top=0;
for(int i=l;i<=r;i++)
{
int x=newnode();
a[x].v=a[x].l=a[x].r=e[i];
a[x].s=1;
while(top&&a[x].k>a[st[top]].k)
{
a[x].c[0]=st[top];
mt(st[top]);
top--;
}
if(a[x].c[0])
a[a[x].c[0]].f=x;
if(top)
{
a[st[top]].c[1]=x;
a[x].f=st[top];
}
st[++top]=x;
}
p=st[1];
while(top)
{
if(top>1)
{
a[st[top-1]].c[1]=st[top];
a[st[top]].f=st[top-1];
}
mt(st[top]);
top--;
}
// dfs(p,0);
}
}
namespace sgt
{
const double alpha=0.75;
struct node
{
int l,r;
int ls,rs;
int x;
int rt;
int s;
};
node a[100010];
int rt;
int cnt;
int *rebuild;
void mt(int p)
{
a[p].s=a[a[p].ls].s+a[a[p].rs].s+1;
a[p].l=(a[p].ls?a[a[p].ls].l:a[p].x);
a[p].r=(a[p].rs?a[a[p].rs].r:a[p].x);
}
void ins(int &p,int x)
{
if(!p)
{
p=++cnt;
a[p].l=a[p].r=x;
a[p].ls=a[p].rs=0;
a[p].x=x;
a[p].rt=0;
a[p].s=1;
treap::insert(a[p].rt,::a[x]);
return;
}
treap::insert(a[p].rt,::a[x]);
if(cmp(x,a[p].x)==1)
{
ins(a[p].ls,x);
mt(p);
if(a[a[p].ls].s>a[p].s*alpha)
rebuild=&p;
}
else
{
ins(a[p].rs,x);
mt(p);
if(a[a[p].rs].s>a[p].s*alpha)
rebuild=&p;
}
}
int tot;
int c[100010];
int d[100010];
void dfs(int &x)
{
if(a[x].ls)
dfs(a[x].ls);
c[++tot]=x;
d[tot]=a[x].x;
if(a[x].rs)
dfs(a[x].rs);
treap::del(a[x].rt);
x=0;
}
int e[100010];
int f[100010];
void build(int &p,int l,int r)
{
if(l>r)
return;
int mid=(l+r)>>1;
p=c[mid];
a[p].x=d[mid];
// for(int i=l;i<=r;i++)
// treap::insert(a[p].rt,::a[d[i]]);
build(a[p].ls,l,mid-1);
build(a[p].rs,mid+1,r);
if(l==r)
e[l]=::a[d[l]];
else
{
int v=::a[d[mid]];
int i;
for(i=mid;i>l&&v<e[i-1];i--)
e[i]=e[i-1];
e[i]=v;
int l1=l,r1=mid+1;
int t1=l;
while(l1<=mid||r1<=r)
if(r1>r||(l1<=mid&&e[l1]<=e[r1]))
f[t1++]=e[l1++];
else
f[t1++]=e[r1++];
for(int i=l;i<=r;i++)
e[i]=f[i];
}
treap::build(a[p].rt,e,l,r);
mt(p);
}
void insert(int x)
{
rebuild=0;
ins(rt,x);
if(rebuild)
{
tot=0;
dfs(*rebuild);
build(*rebuild,1,tot);
}
}
int query(int p,int x,int l,int r)
{
if(!p)
return 0;
if(lcp(a[p].x,x)>=l)
{
if(lcp(a[p].l,x)>=l&&lcp(a[p].r,x)>=l)
return treap::query(a[p].rt,r);
return query(a[p].ls,x,l,r)+query(a[p].rs,x,l,r)+(::a[a[p].x]<=r?1:0);
}
int v=cmp(x,a[p].x);
int s=0;
if(v!=-1)
s+=query(a[p].ls,x,l,r);
if(v!=1)
s+=query(a[p].rs,x,l,r);
return s;
}
}
void add(int x,int y,int v)
{
h[x][0]=v;
f[x][0]=y;
d[x]=d[y]+1;
for(int i=1;i<=16;i++)
{
f[x][i]=f[f[x][i-1]][i-1];
h[x][i]=h[x][i-1]*pw[i-1]+h[f[x][i-1]][i-1];
}
sgt::insert(x);
}
int query(int x,int l,int r)
{
return sgt::query(sgt::rt,x,l,r);
}
int main()
{
open("b");
srand(time(0));
mempool::init();
pw[0]=base;
for(int i=1;i<=17;i++)
pw[i]=pw[i-1]*pw[i-1];
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
d[1]=0;
d[0]=-1;
char c[2];
int x,y,l,r;
for(int i=2;i<=n;i++)
{
scanf("%d%d%s",&x,&y,c);
add(y,x,id(c[0]));
}
int last=1;
for(int i=1;i<=m;i++)
{
scanf("%d",&x);
if(!x)
{
scanf("%d%d%d",&x,&l,&r);
x^=last;
int ans=query(x,l,r);
if(!l&&a[1]<=r)
ans++;
printf("%d\n",ans);
if(ans)
last=ans;
}
else
{
n++;
scanf("%d%d%s",&x,&a[n],c);
x^=last;
add(n,x,id(c[0]));
}
}
return 0;
}

【XSY2773】基因 后缀平衡树 树套树的更多相关文章

  1. BZOJ_3196_二逼平衡树_(树套树,线段树+Treap)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=3196 可以处理区间问题的平衡树. 3196: Tyvj 1730 二逼平衡树 Time Lim ...

  2. [luogu3380][bzoj3196]【模板】二逼平衡树【树套树】

    题目地址 [洛谷传送门] 题目大意 区间查询k的排名,查找k排名的数,单点修改,区间前驱,区间后继. 感想 真的第一次写树套树,整个人都不对了.重构代码2次,发现样例都过不了,splay直接爆炸,可能 ...

  3. 洛谷 P3380 【【模板】二逼平衡树(树套树)】

    其实比想象中的好理解啊 所谓树套树,就是在一棵树的基础上,每一个节点再维护一棵树 说白了,就是为了实现自己想要的操作和优秀的时间复杂度,来人为的增加一些毒瘤数据结构来维护一些什么东西 比如说这道题 如 ...

  4. P3380 【模板】二逼平衡树(树套树)(线段树套平衡树)

    P3380 [模板]二逼平衡树(树套树) 前置芝士 P3369 [模板]普通平衡树 线段树套平衡树 这里写的是线段树+splay(不吸氧竟然卡过了) 对线段树的每个节点都维护一颗平衡树 每次把给定区间 ...

  5. P3380 【模板】二逼平衡树(树套树)

    思路 若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名 若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数 若opt=3 则为操作 ...

  6. bzoj3196: Tyvj 1730 二逼平衡树 树套树

    地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3196 题目: 3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec ...

  7. [洛谷P3380]【模板】二逼平衡树(树套树)

    题目大意:有$5$种操作: $1\;l\;r\;k:$查询$k$在区间$[l,r]$内的排名 $2\;l\;r\;k:$查询区间$[l,r]$内排名为$k$的值 $3\;pos\;k:$把第$pos$ ...

  8. 洛谷 P3380 bzoj3196 Tyvj1730 【模板】二逼平衡树(树套树)

    [模板]二逼平衡树(树套树) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数值 查询k在 ...

  9. 树套树Day1线段树套平衡树bzoj3196

    您需要写一种数据结构,来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查 ...

随机推荐

  1. Pandas基础使用

    Pandas是基于NumPy的一种工具,该工具是为了解决数据分析任务而创建的. 1.导入pandas import pandas as pd 2.pandas数据类型: 1)Series:一维数据类型 ...

  2. Mysql乱码问题总结

    这两天研究了下Mysql的字符集编码和排序规则,有个很典型的问题就是乱码问题.所以小记一下. http://www.jianshu.com/p/4c6a27542df4 http://blog.csd ...

  3. super关键字访问父类成员

    1.super只能出现在子类的方法和构造方法中: 2.super调用构造方法时只能是第一句: 3.super不能访问父类的private成员.

  4. UVA-10375 唯一分解定理

    #include<iostream> #include<string.h> #include<algorithm> #include<math.h> # ...

  5. ubuntu中更改apache默认目录的方法

    如上,在这两个文件中,我都改为/home/www 及/home/www/html

  6. sql学习内容记录

    1.left函数 left(字段,长度):获取指定字段左侧的数据,类似substring函数 2.union / union all 将多个记录合并成一个完整的数据集 3.insert into se ...

  7. Select2 4.0.5 API

    详细属性参考官方API,https://github.com/select2/select2/releases/tag/4.0.5 注:4.0.5版本API与3.x版本有差异,有些属性已废弃,以下列出 ...

  8. jQuery操作复选框checkbox技巧总结 ---- 设置选中、取消选中、获取被选中的值、判断是否选中等

    转载:https://blog.csdn.net/chenchunlin526/article/details/77448168 jQuery操作复选框checkbox技巧总结 --- 设置选中.取消 ...

  9. Day 4-8 hashlib加密模块

    HASH Hash,一般翻译做“散列”,也有直接音译为”哈希”的,就是把任意长度的输入(又叫做预映射,pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值.这种转换是一种压缩映射 ...

  10. java之序列化

    详细内容 连接https://blog.csdn.net/qq_27093465/article/details/78544505 Java 之 Serializable 序列化和反序列化的概念,作用 ...