【SAM+線段樹合併/SA+掃描線】CF1037H Security
【題目】
原題地址
給定一個長度為
的字串
和
個詢問,每次詢問形如
,其中
是一個字串,表示詢問
這個字串中比
字典序大的字典序最小的子串是什麼,如沒有則輸出
。
【解題思路1】
考慮每次詢問如果沒有詢問區間的限制,我們在
上進行匹配,那麼我們每走一次只需要判斷是否有一個比當前走的路大的字尾即可。顯然最後一次可以比詢問串大的分支是最後的答案。
現在有詢問限制,我們可以用一個
的代價列舉接下來匹配的字串是什麼,然後看看再匹配後走到自動機上的點的
集和代表的字串是否在詢問區間中即可。
我們可以用線段樹合併來進行預處理出每個節點的right集。
設 與 同階,複雜度是 的。
【解題思路2】
我們建出原串的
如果我們知道詢問串
和答案串的
的話,問題就轉化為了
下標在
,
下標在
中
最小的串。更具體的說,我們需要求出
下標在
的
最小的串,且
。
我們發現這就是一個二維數點問題,按
排序後掃描線,用線段樹維護詢問即可。
這樣做的複雜度是
的,比
不知道高到哪裡去了。
【參考程式碼】
線段樹合併
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=1e6+10,M=N*26;
int n,Q,rt[N];
char s[N];
struct Segment
{
int sz,ls[M],rs[M];bool w[M];
void pushup(int x){w[x]=w[ls[x]]|w[rs[x]];}
void update(int &x,int l,int r,int p)
{
if(!x) x=++sz;
if(l==r){w[x]=1;return;}
int mid=(l+r)>>1;
if(p<=mid) update(ls[x],l,mid,p);
else update(rs[x],mid+1,r,p);
w[x]=w[ls[x]]|w[rs[x]];
}
int merge(int x,int y,int l,int r)
{
if(!x || !y) return x+y;
int z=++sz,mid=(l+r)>>1;
if(l==r) w[z]=w[x]|w[y];
else
{
ls[z]=merge(ls[x],ls[y],l,mid);
rs[z]=merge(rs[x],rs[y],mid+1,r);
pushup(z);
}
return z;
}
bool query(int x,int l,int r,int L,int R)
{
if(!x || l>r) return 0;
if(L<=l && r<=R) return w[x];
int mid=(l+r)>>1;bool res=0;
if(L<=mid) res|=query(ls[x],l,mid,L,R);
if(R>mid) res|=query(rs[x],mid+1,r,L,R);
return res;
}
}T;
struct SAM
{
int sz,las,fa[N],mx[N],ch[N][26];
vector<int>g[N];
void init(){sz=las=1;}
void extend(int x)
{
int p,q,np,nq;
p=las;las=np=++sz;mx[np]=mx[p]+1;
for(;p && !ch[p][x];p=fa[p]) ch[p][x]=np;
if(!p) fa[np]=1;
else
{
q=ch[p][x];
if(mx[q]==mx[p]+1) fa[np]=q;
else
{
nq=++sz;mx[nq]=mx[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
}
void dfs(int x)
{
for(int i=0;i<(int)g[x].size();++i)
{
int v=g[x][i];
dfs(v);rt[x]=T.merge(rt[x],rt[v],1,n);
}
}
void merge(){for(int i=1;i<=sz;++i) g[fa[i]].pb(i);dfs(1);}
void solve(char *s,int l,int r)
{
int len=strlen(s+1),p=1,pos=0,res=0;
for(int i=1;i<=len+1;++i)
{
int c=i>len?0:(s[i]-'a'+1),flag=0;
for(int j=c;j<26;++j) if(ch[p][j])
{
int x=ch[p][j];
if(T.query(rt[x],1,n,l+i-1,r)){res=j+'a';pos=i-1;break;}
}
if(c && ch[p][c-1]) p=ch[p][c-1]; else break;
}
if(!res){puts("-1");return;}
for(int i=1;i<=pos;++i) putchar(s[i]);
putchar(res);puts("");
}
}S;
int main()
{
#ifndef ONLINE_JUDGE
freopen("CF1037H.in","r",stdin);
freopen("CF1037H.out","w",stdout);
#endif
scanf("%s",s+1)
相關推薦
【SAM+線段樹合併/SA+掃描線】CF1037H Security
【題目】 原題地址 給定一個長度為
n
n
n的字串
【SAM+線段樹合併】LGP4770 [NOI2018]你的名字
【題目】 原題地址 給定一個字串
S
S
S,多組詢問給定字串
bzoj 1396: 識別子串【SAM+線段樹】
貢獻 ios mem scan turn oid const -i zoj 建個SAM,符合要求的串顯然是|right|==1的節點多代表的串,設si[i]為right集合大小,p[i]為right最大的r點,這些都可以建出SAM後再parent樹上求得
然後對弈si[i]
[NOI2018]你的名字(SAM+線段樹合併)
考慮l=1,r=n的68分,對S和T建SAM,對T的SAM上的每個節點,計算它能給答案帶來多少貢獻。
T上節點x代表的本質不同的子串數為mx[x]-mx[fa[x]],然後需要去掉所代表子串與S的最長公共子串的長度。
從1到length(T)掃一遍,SAM基本操作求出每個字首與S的最長公共子串。
答案為
【POJ 2482】 Stars in Your Window(線段樹+離散化+掃描線)
d+ opera algorithm ans som lov ble word wait
【POJ 2482】 Stars in Your Window(線段樹+離散化+掃描線)
Time Limit: 1000MS
M
【題解】[牛客網NOIP賽前集訓營-提高組(第一場)]C.保護 LCA+線段樹動態開點+線段樹合併
題目連結 ___
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int n,m,hd[N],to
【bzoj2333 & luoguP3273】棘手的操作(線段樹合併)
題目傳送門:bzoj2333 luoguP3273
這操作還真“棘手”。。聽說這題是可並堆題?然而我不會可並堆。於是我就寫了線段數合併,然後調了一晚上,資料結構毀一生!!!QAQ……
其實這題也可以把合併強行看成樹上的關係然後dfs序後直接線段樹的,然而我菜啊。。看到連邊就只能想到線
【CF666E】Forensic Examination - 廣義字尾自動機+線段樹合併
廣義SAM專題的最後一題了……呼
題意:
給出一個長度為$n$的串$S$和$m$個串$T_{1\cdots m}$,給出$q$個詢問$l,r,pl,pr$,詢問$S[pl\cdots pr]$在$T_l\cdots T_r$中哪個串出現次數最多,出現了多少次。
$1\leq n,q\leq 10^5,1
POJ1151 (HDU 1542) Atlantis【矩形面積並,線段樹+離散化+掃描線模板】
The input consists of several test cases. Each test case starts with a line containing a single integer n (1 <= n <= 100) of available maps. The n f
【整體二分+線段樹合併+tarjan】LGP5163 WD與地圖
【題目】 原題地址 給定一幅有向圖,支援三種操作:
刪去一條邊
給一個點權值增加
x
x
【LUOGU???】WD與地圖 整體二分 線段樹合併
題目大意
有一個簡單有向圖。每個點有點權。
有三種操作:
修改點權
刪除一條邊
詢問和某個點在同一個強連通分量中的點的前 \(k\) 大點權和。
\(n\leq 100000,m,q\leq 200000\)
題解
把操作反過來,每次只有加邊操作。
用線段樹
uoj#418. 【集訓隊作業2018】三角形(線段樹合併)
傳送門
好迷啊……膜一下ljz
考慮每個操作,如果把操作按先後順序放到序列上的話,操作一就是把\(w_i\)的石子放到某個節點,那麼就是在序列末端加入\(w_i\),然後根據貪心肯定要把它所有兒子的石子拿走,也就是要減去\(\sum w_{son}\)
那麼每個點的答案就是序列的最大字首
因為父親節點
【bzoj4530】[Bjoi2014]大融合 並查集+線段樹合併
線段樹合併好神啊,表示我這種傻逼只能想到樹剖O(nlog^2n)做法
先把原樹建出來,每次查詢就等價於計運算元節點的size*(父親節點所在聯通塊的大小-子節點的size)
用並查集找到節點的祖先,維護子樹size
這個東西可以用線段樹合併來做,查詢就是查詢dfs序上的一段
【BZOJ2212】【POI2011】Tree Rotations(線段樹合併)
Description
Solution
對於每個節點有一棵權值線段樹,向上遞迴時合併同時計算逆序對即可。
Source
/*****************************
【usaco2017Jan】Promotion Counting的解析——線段樹合併入門
這道題貌似是道水題,看見那麼多人寫樹狀陣列,正好我最近又要學習線段樹合併,於是發了這篇線段樹合併入門blog.
一:線段樹合併的概念.
首先我們先介紹完線段樹合併再來分析題目.
線段樹合併是基於動態開點線段樹的,一開始建立n棵線段樹,每棵線段樹只有一條鏈是記錄著的.
【生成函式+拉格朗日插值+整體DP+線段樹合併】LGP4365 [九省聯考2018]祕密襲擊coat
【題目】
原題地址
給定一棵NNN個節點的樹,點權1∼W1 \sim W1∼W,求樹的每一個連通塊的第KKK大點權之和。
【解題思路】
以下均來自這裡
首先可以轉化一下題目。
Ans=∑S∈TkthofS=∑i=1Wi×∑S∈T[kthofS==i]=∑i=1W
【JZOJ 3397】 雨天的尾巴 線段樹合併
Description
一棵樹,有若干操作,每個操作包含三個數x,y,z,表示在樹上從x到y的路徑上的所有點的z顏色的權值+1。所有操作結束後詢問每個點權值最大的顏色是什麼。
對於100% 的資料,1 <= n,m <= 100000; 1 &l
hdu 1540/POJ 2892 Tunnel Warfare 【線段樹區間合並】
article 遞歸 線段 tdi != lines nor sample eight
Tunnel Warfare
Time Limi
BZOJ2333 [SCOI2011]棘手的操作 【離線 + 線段樹】
一行 amp pos fin blog 最大的 其中 getchar() 兩種 題目
有N個節點,標號從1到N,這N個節點一開始相互不連通。第i個節點的初始權值為a[i],接下來有如下一些操作:
U x y: 加一條邊,連接第x個節點和第y個節點
A1 x v: 將第x個節
loj2537 「PKUWC2018」Minimax 【概率 + 線段樹合並】
tdi size pri 題目 概率 log mini source ima 題目鏈接
loj2537
題解
觀察題目的式子似乎沒有什麽意義,我們考慮計算出每一種權值的概率
先離散化一下權值
顯然可以設一個\(dp\),設\(f[i][j]\)表示\(i\)節點權值為\(j