codeforcs 1063F. String Journey - dp -SAM - 主席樹/線段樹合併 - 子串定位 - 倍增
題目大意:
給你一個長為
的字串
,求最大的
,使得能夠找出
個不重疊的子串
,使得
,並且
是
的子串。
題解:
首先觀察性質。
顯然和這種“當前串是上一個的子串然後最大化序列長度”之類的,可以證明一定存在一種最優解,使得每個串都是上一個串的字首或者字尾(注意都有可能,並不一定全部都是字尾或者字首,因為證明過程是,例如他不是字首,我想讓他變成字首就把大的串的字首砍掉;但是由於他已經是字尾了可能砍掉後兩個串相等就gg了),並且進一步的說,可以讓相鄰兩個串的長度差恰好是1,再進一步的說,最後最短的串可以讓他長度是1(否則每次切掉那個沒用的位置,做若干輪即可)。很快發現答案不超過根號,於是你可以樸素的設f(i,j)表示第i個位置向後延伸j的長度是否可行,列舉下一個位置是
的字首還是字尾,然後顯然下一次出現越早越好,因此直接找到那個位置,用那個轉移即可。如果實現優秀可以做到不帶log的根號演算法。
但是又會發現,f(i,j)作為一個bool陣列,其一段字首是1,之後是0.因此設f(i)表示i這個位置最遠能夠延伸多少,二分答案做上述過程即可兩個log。
顯然求下一個位置無論如何都優化不掉了(ba),考慮去掉前面的二分答案。
進一步觀察性質,很快發現,假設當前f(i)=j,有兩種情況,第一種是下一個位置取了個字尾,那麼f(i+1)這個位置就和和下一個位置相等,因此
;第二種情況是下一個位置是字首,看似此時
其實不然,你還是可以選出
的答案。因此有
恆成立。注意到
,而之前的等價於
,因此用類似倍增陣列求height的方法即可做到均攤線性的列舉答案。
求下一個位置只要先在SAM上倍增做子串定位,然後上線段樹合併/主席樹即可。實測如果實現不好(即你開了倍增陣列、ch陣列開了26),還寫了線段樹合併會MLE。一個優化方法是發現倍增陣列和ch陣列使用時間不重疊可以公用,另一種方法是使用主席樹可以卡掉很大的空間常數。這樣就時空一個log了。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define N 500010
#define SIG 27
#define LOG 20
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
#define S(rt) (((rt)==NULL)?0:((rt)->s))
#define CH(rt,w) (((rt)==NULL)?NULL:((rt)->ch[w]))
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
struct edges{ int to,pre; };
struct segment{ int s;segment *ch[2]; };
int f[N];
int update(segment* &x,segment* y,int l,int r,int p)
{
x=new segment,x->s=S(y)+1;int mid=(l+r)>>1;
x->ch[0]=CH(y,0),x->ch[1]=CH(y,1);if(l==r) return 0;
if(p<=mid) update(x->ch[0],CH(y,0),l,mid,p);
if(mid<p) update(x->ch[1],CH(y,1),mid+1,r,p);
return 0;
}
int query(segment* x,segment* y,int l,int r,int p)
{
int mid=(l+r)>>1,ans;if(S(x)-S(y)==0||p>r) return 0;if(l==r) return r;
if((ans=query(CH(x,0),CH(y,0),l,mid,p))) return ans;
return query(CH(x,1),CH(y,1),mid+1,r,p);
}
struct SAM{
int node_cnt,rt,las,val[N*2],ch[N*2][SIG],dfc,in[N<<1],rp[N<<1];
int up[N<<1][LOG],fa[N*2],ps[N<<1],n,h[N<<1],etop,out[N<<1],tms[N<<1];
struct edges { int to,pre; }e[N<<1];segment *seg[N<<1];
inline int add_edge(int u,int v) { return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop; }
SAM() { n=node_cnt=0,rt=las=new_SAM_node(0); }
inline int new_SAM_node(int v,int x=0) { return val[x=++node_cnt]=v,x; }
inline int extend(int w)
{
int p=las,np=new_SAM_node(val[p]+1);
while(p&&!ch[p][w]) ch[p][w]=np,p=fa[p];
if(!p) fa[np]=rt;
else{
int q=ch[p][w],v=val[p]+1;
if(val[q]==v) fa[np]=q;
else{
int nq=new_SAM_node(v);
memcpy(ch[nq],ch[q],sizeof ch[q]);
fa[nq]=fa[q],fa[q]=fa[np]=nq;
while(p&&ch[p][w]==q) ch[p][w]=nq,p=fa[p];
}
}
return las=np,ps[++n]=np,rp[np]=n;
}
int dfs(int x)
{
in[x]=dfc+1;if(rp[x]) tms[++dfc]=rp[x];
for(int i=h[x];i;i=e[i].pre) dfs(e[i].to);
return out[x]=dfc;
}
inline int build()
{
dfc=0,getedge(),getup(),dfs(rt),seg[0]=NULL;
rep(i,1,n) update(seg[i],seg[i-1],1,n,tms[i]);
return 0;
}
inline int getedge() { rep(i,1,node_cnt) if(fa[i]) add_edge(fa[i],i);return 0; }
inline int getup()
{
rep(i,1,node_cnt) up[i][0]=fa[i];
rep(j,1,LOG-1) rep(i,1,node_cnt) up[i][j]=up[up[i][j-1]][j-1];
return 0;
}
inline int getnode(int r,int len)
{
int x=ps[r];
for(int i=LOG-1;i>=0;i--)
if(up[x][i]&&val[up[x][i]]>=len) x=up[x][i];
return x;
}
inline int query_nxt(int l,int r,int t)//s[q,q+len-1]=s[l,r],q>=t
{
int len=r-l+1,x=getnode(r,len);
if(in[x]>out[x]||t+len-1>n) return 0;
int ans=query(seg[out[x]],seg[in[x]-1],1,n,t+len-1);
if(ans) return ans-len+1;return 0;
}
}s;
inline int cant(int l,int x,int t=0)
{
t=s.query_nxt(l,l+x-2,l+x);if(t&&f[t]>=x-1) return 0;
t=s.query_nxt(l+1,l+x-1,l+x);if(t&&f[t]>=x-1) return 0;
return 1;
}
char str[N];
int main(
相關推薦
codeforcs 1063F. String Journey - dp -SAM - 主席樹/線段樹合併 - 子串定位 - 倍增
題目大意: 給你一個長為
n
n
n的字串
Codeforces 666E Forensic Examination SAM+權值線段樹
namespace return 匹配 roo tchar long bits codeforce XA 第一次做這種$SAM$帶權值線段樹合並的題 然而$zjq$神犇看完題一頓狂碼就做出來了 $Orz$
首先把所有串當成一個串建$SAM$ 我們對$SAM$上每個點 建一棵
字串(tjoi2016,heoi2016,bzoj4556)(sam(字尾自動機)+線段樹合併+倍增+二分答案)
佳媛姐姐過生日的時候,她的小夥伴從某東上買了一個生日禮物。生日禮物放在一個神奇的箱子中。箱子外邊寫了 一個長為\(n\)的字串\(s\),和\(m\)個問題。佳媛姐姐必須正確回答這\(m\)個問題,才能開啟箱子拿到禮物,升職加薪,出任CE O,嫁給高富帥,走上人生巔峰。每個問題均有\(a,b,c,d\)四個引
2018黑龍江省賽 A Sequence Game 主席樹+st表 / 主席樹+線段樹 / st表+莫隊+離散化
7218: A Sequence Game
時間限制: 1 Sec 記憶體限制: 128 MB 提交: 128 解決: 32 [提交] [狀態] [討論版] [命題人:admin]
題目描述
One da
bzoj 1901 ZOJ 2112 Dynamic Rankings [樹狀陣列套主席樹] [線段樹套平衡樹]
Dynamic Rankings
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 6900 Solved: 2870
Description
給定一個含有n個數的序列a[1],a[2],a[3]……a[
動態區間第k小(主席樹+線段樹套樹狀陣列)
靜態區間第k小問題,是給你一個序列,每次詢問序列中的一個區間中的第k小數,這個問題用普通的主席樹就可以解決。動態區間第k小問題就是在靜態的基礎上加上了修改操作,也就是每次除了詢問區間第k小之外,還可以修改序列中的某個數。因為這裡涉及到了修改操作,我們用只用主席樹
[SPOJ COT3] SG函數 Trie樹 線段樹合並
swa 線段 std line etc 分析 eight sin stdout 題目
分析
實現
#include <cstdio>
#include <cstring>
#include <cstdlib>
bzoj3685普通van Emde Boas樹 線段樹
while 個數 最大 time push 線段 != submit using 3685: 普通van Emde Boas樹Time Limit: 9 Sec Memory Limit: 128 MBSubmit: 1932 Solved: 626[Submit][S
bzoj3196 二逼平衡樹——線段樹套平衡樹
online truct str clu turn fin lan modify .com 題目:https://www.lydsy.com/JudgeOnline/problem.php?id=3196
人生中第一棵樹套樹!
寫了一個晚上,成功卡時 9000ms+ 過了!
摧毀圖狀樹 - 線段樹 - 倍增
題目大意: 給你一棵無權樹,對於每個k=1…n求: 每次你可以給一個點x到其k級祖先的路徑上的所有點打上刪除標記。問最少多少次可以把所有點打上刪除標記。
n
≤
jzoj5943. 【NOIP2018模擬11.01】樹(線段樹)
5943. 【NOIP2018模擬11.01】樹
Description
Input
第一行一個整數 n 表示序列長度, 接下來一行 n 個整數描述這個序列.
第三行一個整數 q 表示操作次數, 接下來 q 行每行一次操作, 格式同題目描述.
Output
輸
bzoj3196 Tyvj 1730 二逼平衡樹 線段樹套splay
Description
您需要寫一種資料結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:
1.查詢k在區間內的排名
2.查詢區間內排名為k的值
3.修改某一位值上的數值
4.查詢k在區間內的前驅(前驅定義為小於x,且最大的數)
5.查詢k在區
poj 2155 matrix 二維線段樹 線段樹套線段樹
題意
一個$n*n$矩陣,初始全為0,每次翻轉一個子矩陣,然後單點查詢
題解
任意一種能維護二維平面的資料結構都可以
我這裡寫的是二維線段樹,因為四分樹的寫法複雜度可能會退化,因此考慮用樹套樹實現二維線段樹
簡單來說就是每個點都維護了一顆線段樹...
因為二維線段樹難以實現pushdown,而他的
【BZOJ3600】沒有人的算術 - 替罪羊樹+線段樹
題意:
題解:
Orz vfleaking……真·神題
做法大概是先把題意中定義的“數”都賦一個實數權值,用平衡樹來維護整個從大到小排序過的序列,再用線段樹查詢最值;
這樣做為什麼是對的?考慮插入一個數$x$,我們已經知道了$x_L$和$x_R$在序列中的位置,就可以直接每
bzoj3682 Phorni 字尾平衡樹+線段樹
Description
給定一個字串和n個pos,要求資瓷:
開頭插入一個字元c
將第x個pos改為y
查詢第l到r個pos中所代表的最大字尾 強制線上
Solution
久違的串串題。感覺字尾平衡樹好像挺好寫的,線上還能logn。
bzoj 3730: 震波 (點分樹+線段樹)
最近敲這種一言不合程式碼就150+的點分樹題已經麻木了QAQ
對於每個重心我們開兩顆線段樹(動態開):
一顆線段樹中,對點分樹中以u為根的子樹中的節點v,我們以他距u的距離為下標,點權為值插入線段樹
另一顆裡,對點分治中以u為根的子樹中的節點v,我們以他距u的父親節點距離為下標,點權為值插入線段
藍橋杯 校外的樹 線段樹
#include <iostream>
#include <stdio.h>
#define N 10010
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
資料結構----線段樹----線段樹的定義與構造
一、什麼是線段樹?
1、線段樹是一棵二叉樹,樹中的每一個結點表示了一個區間[a,b]。
2、每一個葉子節點表示的是一個單位區間。
3、根節點表示的是“整體”的區間。
4、對於每一個非葉結點所表示的區間
bzoj 3196 && luogu 3380 JoyOI 1730 二逼平衡樹 (線段樹套Treap)
put clas 結構 online input 維護 amp bbs 查詢 鏈接:https://www.lydsy.com/JudgeOnline/problem.php?id=3196
題面;
3196: Tyvj 1730 二逼平衡樹
Time Limit
[CF1063F]String Journey[字尾陣列+線段樹]
題意
在 \(S\) 中找出 \(t\) 個子串滿足 \(t_{i+1}\) 是 \(t_{i}\) 的子串,要讓 \(t\) 最大。
\(|S| \leq 5\times 10^5\).
分析
定義狀態 \(f_{i}\) 表示從 \(i\) 出發能夠得到的最長的 \(journey\) .