有一棵樹,每條邊上面都有乙個字母。每個點還有乙個特徵值\(a_i\)。
定義乙個節點\(i\)對應的字串為從這個點到根的路徑上所有邊按順序拼接而成的字串\(s_i\)。
有\(m\)次操作:
\(n,m\leq 40000\)
然後就會發現這是一道非常水的題了。
維護字尾平衡樹,每個點再維護關鍵字為這個區間內每個點的特徵值的一棵平衡樹,也就是樹套樹。
字尾平衡樹可以做到\(o(1)\)比較大小,但是這題不需要。
(其實\(o(1)\)求上面那個東西會跑的很快,但是我很懶。)
時間複雜度:\(o((n+m)\log^2 (n+m))\)
#include#include#include#include#include#includeusing namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pairpii;
int rd()
void open(const char *s)
int id(char c)
return 0;
}namespace mempool
int get()
void push(int 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)
void del(int &p)
int query(int p,int v)
void dfs(int p,int f)
int st[100010];
void build(int &p,int *e,int l,int r)
if(a[x].c[0])
a[a[x].c[0]].f=x;
if(top)
st[++top]=x;
} p=st[1];
while(top)
mt(st[top]);
top--;
}// dfs(p,0); }}
namespace sgt
; node a[100010];
int rt;
int cnt;
int *rebuild;
void mt(int p)
void ins(int &p,int x)
treap::insert(a[p].rt,::a[x]);
if(cmp(x,a[p].x)==1)
else
}int tot;
int c[100010];
int d[100010];
void dfs(int &x)
int e[100010];
int f[100010];
void build(int &p,int l,int r)
treap::build(a[p].rt,e,l,r);
mt(p);
} void insert(int x) }
int query(int p,int x,int l,int r)
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)
sgt::insert(x);
}int query(int x,int l,int r)
int main()
int last=1;
for(int i=1;i<=m;i++)
else
}return 0;
}
字尾平衡樹
如果需要動態維護字尾陣列,支援在字串前端插入乙個字元,詢問字尾的大小關係,如何做呢?這是乙個不斷插入的問題,可以從增量的角度考慮。我們在前端插入乙個字元,其實就是插入了乙個新的字尾。我們的問題其實就是這個字尾排名多少。我們可以用平衡樹維護一下字尾陣列,從根節點開始二分比較這個字尾的大小,看看它應該被...
字尾平衡樹學習筆記
給定乙個空串s 操作1 代表在 s前加入乙個字母使之成為新s 操作2 代表在詢問在當前 s中有多少連續子串等於給定串t 假設我們已經有了串 s的字尾平衡樹 插入乙個字母c 我們用s i代表原串 s 從第 i個字元開始的字尾 則字尾 cs 與 任意乙個字尾 si 的大小關係可以用 c與s i 的第乙個...
總結 字尾平衡樹學習筆記
字尾平衡樹,就是動態的維護字尾陣列,可以 o log n 在末尾插入字元,o log n 查詢 rank,sa 但是由於是維護的字尾資訊,所以插入只能在末尾插入字元 然後轉化成在開頭加乙個字元 相當於新增乙個字尾。方法一 我們需要一種能比較兩個字尾大小的方法,最簡單就是二分 hash,o log n...