模板记忆
图论
虚树
对 dfn 排序,然后在中间插入两两 lca,再排序去重。最后求出再求两两 lca 连边。注意第一个点不连。
void build(){
tot=0;
sort(h+1,h+1+m,cmp);
for(int i=1;i<m;i++){
int Lca=lca(h[i],h[i+1]);
a[++tot]=h[i]; a[++tot]=Lca;
}
a[++tot]=h[m];
sort(a+1,a+1+tot,cmp);
tot=unique(a+1,a+1+tot)-a-1;
for(int i=1;i<tot;i++){
int Lca=lca(a[i],a[i+1]);
add(Lca,a[i+1]);
}
}
tarjan 求 LCA
刚进来打上 vis=1 标记,离开打上 vis=2 标记。搜索到子树的时候,并查集将子树并进去。遍历对于当前节点的询问,如果另一个点已经离开了或者两点相同(注意别忘了),就搜一下并查集。
void tarjan(int x){
vis[x]=1;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(vis[y]) continue;
tarjan(y); fa[y]=x;
}
for(int i=0;i<query[x].size();i++){
int y=query[x][i]; int id=query_id[x][i];
if(vis[y]==2||x==y) ans[id]=find(y);
}
vis[x]=2;
}
dfs序求 LCA
void dfs(int u,int father){
pa[0][dfn[u]=++tot]=father;
for(auto v:G[u])
if(v!=father) dfs(v,u);
}
int cmp(int x,int y){ return dfn[x]<dfn[y]?x:y; }
int lca(int u,int v){
if(u==v) return u;
u=dfn[u]; v=dfn[v];
if(u>v) swap(u,v);
u++; int k=lg[v-u+1];
return cmp(pa[k][u],pa[k][v-(1<<k)+1]);
}
网络流
bool bfs(int s,int t){
memset(d,0,sizeof(d));
while(!q.empty()) q.pop();
d[s]=1; now[s]=head[s]; q.push(s);//清空 now(s)
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i;i=edges[i].Next){
int v=edges[i].to;
//注意两个条件
if(!edges[i].flow||d[v]) continue;
//清空 now(v)
q.push(v); now[v]=head[v]; d[v]=d[u]+1;
if(v==t) return 1;
}
}
return 0;
}
long long dinic(int s,int t,long long flow){
if(s==t) return flow;
long long rest=flow,k;
for(int i=now[s];i&&rest;i=edges[i].Next){//rest有剩余
int v=edges[i].to; now[s]=i;
//注意d的条件
if(!edges[i].flow||d[v]!=d[s]+1) continue;
k=dinic(v,t,min(rest,edges[i].flow));
if(!k) d[v]=0;
edges[i].flow-=k; edges[i^1].flow+=k; rest-=k;
}
return flow-rest;
}
树链剖分
long long query_1(int u,int v){
long long res=0;
while(top[u]!=top[v]){
if(depth[top[u]]<depth[top[v]]) swap(u,v);
res=(res+query(1,id[top[u]],id[u]))%mod;
u=fa[top[u]];
}
if(depth[u]>depth[v]) swap(u,v);
res=(res+query(1,id[u],id[v]))%mod;
return res;
}
树哈希
ull f(ull x){
return ((x*x*4243298829+x*432092)/8)^321432309+x*43243290;
}
ull dfs(int u,int fa){
vector<ull> h;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v==fa) continue;
h.push_back(dfs(v,u));
}
ull ret=1;
for(int i=0;i<(int)h.size();i++) ret+=f(h[i]);
return ret;
}
tarjan
边双
如果要缩点就遍历所有边,当种类不同时连边。
void tarjan(int u,int in_edge){
dfn[u]=low[u]=++cnt;
for(int i=head[u];i;i=edges[i].Next){
int v=edges[i].to;
if(!dfn[v]){
tarjan(v,i);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u])
bridge[i]=bridge[i^1]=1;
}else if(i!=(in_edge^1)) low[u]=min(low[u],dfn[v]);
}
}
void dfs(int u){
c[u]=dcc; s[dcc].push_back(u);
for(int i=head[u];i;i=edges[i].Next){
int v=edges[i].to;
if(c[v]||bridge[i]) continue;
dfs(v);
}
}
点双
如果要缩点的话,给每个割点赋一个编号(顺着 V-DCC 的编号)。然后遍历每一个 V-DCC,找到其中的割点,然后连边割点和 V-DCC。
void tarjan(int u){
dfn[u]=low[u]=++cnt; st[++top]=u; int flag=0;
if(u==root&&head[u]==0){
Dcc[++dcc].push_back(u); return ;
}
for(int i=head[u];i;i=edges[i].Next){
int v=edges[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
flag++; dcc++; int z;
if(u!=root||flag>1) cut[u]=1;
do{
z=st[top--];
Dcc[dcc].push_back(z);
}while(z!=v);
Dcc[dcc].push_back(u);
}
}else low[u]=min(low[u],dfn[v]);
}
}
动态规划
按层扩展
void prework(){
memset(road,inf,sizeof(road));
for(int i=0;i<(1<<n);i++){
expand[i]=i;
for(int j=1;j<=n;j++){
if(i>>(j-1)&1){
road[i][j]=0;
for(int k=1;k<=n;k++){
if(a[j][k]>=inf) continue;
expand[i]|=1<<(k-1);
road[i][k]=min(road[i][k],a[j][k]);
}
}
}
}
for(int i=0;i<(1<<n);i++){
for(int j=(i-1)&i;j;j=(j-1)&i){
if((i&j)==j&&(i&expand[j])==i){
valid[i].push_back(j);
int sum=0;
for(int k=1;k<=n;k++)
if((j^i)>>(k-1)&1) sum+=road[j][k];
cost[i].push_back(sum);
}
}
}
return ;
}
树上背包
重新动态计算 Size,第一层枚举卡住上届,第二层枚举上下界都要卡住。
Size[u]=1;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v==fa) continue;
Dp(v,u); Size[u]+=Size[v];
for(int k=min(Size[u],m);k>=1;k--)
for(int x=max(0,k-Size[u]+Size[v]);x<=min(k,Size[v]);x++)
}
数位
limit 记录前导 \(0\) 限制,lead 为是否贴着上界。如果二者都为 \(0\) 才能记录进 vis。
long long dfs(bool limit,bool lead,int pos,int num,int sum){
if(pos==0&&sum==0) return 0;
if(pos==0) return (sum==mod)&&(num==0);
if(vis[pos][num][sum]&&!limit&&!lead) return f[pos][num][sum];
int up=limit?a[pos]:9; long long res=0;
for(int i=0;i<=up;i++)
res+=dfs(limit&(i==a[pos]),lead&(i==0),pos-1,(num*10+i)%mod,sum+i);
if(!limit&&!lead) vis[pos][num][sum]=1,f[pos][num][sum]=res;
return res;
}
斜率优化
int l,r; l=r=1; q[l]=0; dp[0]=0;
for(int i=1;i<=n;i++){
while(l<r&&slope(q[l],q[l+1])>(double)K(i)) l++;
int j=q[l];
dp[i]=dp[j]+A(i,j)+B(i);
while(l<r&&slope(q[r-1],q[r])<=slope(q[r],i)) r--;
q[++r]=i;
}
CDQ解斜率优化
先对时间序排,然后左右各按横坐标和斜率排一遍
void cdq(int l,int r){
if(l==r){ dp[l]=max(dp[l],dp[l-1]); return;}
sort(s+l,s+r+1);
int mid=(l+r)>>1;
cdq(l,mid);
sort(s+l,s+mid+1,cmpX);
sort(s+mid+1,s+r+1,cmpK);
int ll=1,rr=0;
for(int i=l;i<=mid;i++){
while(ll<rr&&slope(q[rr-1],q[rr])<slope(q[rr],s[i])) rr--;
q[++rr]=s[i];
}
for(int i=mid+1;i<=r;i++){
while(ll<rr&&k(s[i])<slope(q[ll],q[ll+1])) ++ll;
dp[s[i]]=max(dp[s[i]],a[s[i]]*x(q[ll])+b[s[i]]*y(q[ll]));
}
cdq(mid+1,r);
}
wqs二分
注意最后要再 check(l)
一遍或者途中就记下合法答案。否则可能在 check(mid) 后答案变动。
二维四边形不等式
for(int k=p[l][r-1];k<=p[l+1][r];k++){
int t=dp[l][k]+dp[k+1][r]+s[r]-s[l-1];
if(dp[l][r]>t){
dp[l][r]=t;
p[l][r]=k;
}
}
决策单调性之队列
void insert(int i,int &l,int &r){
int w=-1;
while(l<=r){
if(calc(q[r].l,i)<=calc(q[r].l,q[r].x)) w=q[r--].l;//队尾整体差于i,弹出队尾
else{
if(calc(q[r].r,q[r].x)>=calc(q[r].r,i)){//队尾前半部分优于i,后半部分差于i
int l1=q[r].l,r1=q[r].r;
while(l1<r1){
int mid=(l1+r1)>>1;
if(calc(mid,i)>calc(mid,q[r].x)) l1=mid+1;
else r1=mid;
}
q[r].r=l1-1; w=l1;
}
break;
}
}
if(w!=-1) q[++r]=(Q){w,n,i};
}
for(int i=1;i<=n;i++){
int j=q[l].x;
f[i]=calc(i,j);
pre[i]=j;
while(l<=r&&q[l].r<=i) l++;//排除无用部分p[1~i-1]
q[l].l=i+1;
insert(i,l,r);
}
矩阵写法
struct Matrix{
ll e[3][3];
ll& operator () (int a,int b) { return e[a][b]; }
}M;
Matrix operator * (Matrix a,Matrix b){
Matrix c;
for(int i=0;i<=2;i++)
for(int j=0;j<=2;j++) c(i,j)=inf;
for(int i=0;i<=2;i++)
for(int j=0;j<=2;j++)
for(int k=0;k<=2;k++) c(i,j)=min(c(i,j),a(i,k)+b(k,j));
return c;
}
动态 dp
第二次 dfs 动态维护全局矩阵与 \(f\) 值
修改时,先改全局矩阵值,然后 pushup 修改平衡树矩阵值。
最后如果父亲在同一颗平衡树内直接 pushup。
不同平衡树内说明链变化了,于是先让父亲(在另一条重链上)的全局矩阵减去旧的 \(f\) 值,再更新 \(top\) 的 \(f\) 值,最后让父亲的全局矩阵加上新的 \(f\) 值。
`
M[fa[u]].mat[0][0]-=max(f[top[u]][0],f[top[u]][1]); M[fa[u]].mat[1][0]-=f[top[u]][0];
f[top[u]][0]=max(mul[u].mat[0][0],mul[u].mat[0][1]); f[top[u]][1]=max(mul[u].mat[1][0],mul[u].mat[1][1]);
M[fa[u]].mat[0][0]+=max(f[top[u]][0],f[top[u]][1]); M[fa[u]].mat[1][0]+=f[top[u]][0];
M[fa[u]].mat[0][1]=M[fa[u]].mat[0][0];
pushup(u=fa[u]); }
}
`
数学
线性逆元
\(inv_i=(p-p/i)*inv_{p\bmod i} \bmod p\)
矩阵
void mul1(ll A[maxn],ll B[maxn][maxn]){
ll c[maxn]; memset(c,0,sizeof(c));
for(int j=1;j<=3;j++)
for(int k=1;k<=3;k++) c[j]=(c[j]+A[k]*B[k][j])%mod;
memcpy(A,c,sizeof(c));
}
void mul2(ll A[maxn][maxn]){
ll c[maxn][maxn]; memset(c,0,sizeof(c));
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
for(int k=1;k<=3;k++) c[i][j]=(c[i][j]+A[i][k]*A[k][j])%mod;
memcpy(A,c,sizeof(c));
}
高精度
struct A{
int a[maxl],len;
inline A operator * (const A x) const {
A ans;
memset(ans.a,0,sizeof(ans.a));
for(int i=1;i<=len;i++)
for(int j=1;j<=x.len;j++){
ans.a[i+j-1]+=a[i]*x.a[j];
ans.a[i+j]+=ans.a[i+j-1]/10;
ans.a[i+j-1]%=10;
}
ans.len=len+x.len-1;
while(!ans.a[ans.len]&&ans.len>=2) --ans.len;
if(ans.a[ans.len+1]) ++ans.len;
return ans;
}
}n1,n2;
struct A{
int a[maxl],len;
inline A operator + (const A x) const {
A ans;
memset(ans.a,0,sizeof(ans.a));
for(int i=1;i<=max(len,x.len);i++){
ans.a[i]+=a[i]+x.a[i];
ans.a[i+1]=ans.a[i]/10;
ans.a[i]%=10;
}
ans.len=max(len,x.len);
if(ans.a[ans.len+1]) ++ans.len;
return ans;
}
}n1,n2;
struct A{
int a[maxl],len;
inline A operator - (const A x) const {
A ans;
memset(ans.a,0,sizeof(ans.a));
ans.len=0;
long long num=0;
for(int i=1;i<=len;i++){
ans.a[i]+=a[i]-x.a[i];
if(ans.a[i]<0) ans.a[i]+=10,ans.a[i+1]-=1;
}
ans.len=len;
while(!ans.a[ans.len]&&ans.len>=2) ans.len--;
return ans;
}
}n1,n2;
struct A{
int a[maxl],len;
inline A operator / (const long long x) const {
A ans;
memset(ans.a,0,sizeof(ans.a));
ans.len=0;
long long num=0;
for(int i=len;i>=1;i--){
num=num*10+a[i];
ans.a[i]=num/x;
num%=x;
if(!ans.len&&ans.a[i]) ans.len=i;
}
if(!ans.len) ans.a[++ans.len]=0;
return ans;
}
}n1;
数据结构
树套树
void add(int p,int v,int x){
insert(root[p],x);
if(l(p)==r(p)) return ;
int mid=(l(p)+r(p))>>1;
if(v<=mid) add(p*2,v,x);
else add(p*2+1,v,x);
}
int query(int p,int l,int r,int k){
if(l(p)==r(p)) return l(p);
int Lc=p*2,Rc=p*2+1;
int cnt=getrank(root[Lc],r)-getrank(root[Lc],l-1);
if(cnt>=k) return query(Lc,l,r,k);
else return query(Rc,l,r,k-cnt);
}
李超线段树
double val(int num,int x){ return a[num]+d[num]*(x-1); }
void update(int id){
int p=1;
while(p<=n){
if(val(id(p),l(p))<val(id,l(p))&&val(id(p),r(p))<val(id,r(p))){
id(p)=id; break;
}
if(val(id(p),l(p))>val(id,l(p))&&val(id(p),r(p))>val(id,r(p))) break;
int mid=(l(p)+r(p))>>1;
if(val(id(p),mid)<val(id,mid)) swap(id,id(p));
if(d[id(p)]>d[id]) p=lc;
else p=rc;
}
}
double query(int y){
int p=1; double ans=-1e9;
while(p<=n){
ans=max(ans,val(id(p),y));
int mid=(l(p)+r(p))>>1;
if(y<=mid) p=lc;
else p=rc;
}
if(ans==-1e9) ans=0;
return ans;
}
笛卡尔树
编号满足二叉搜索树,权值满足小根堆。
注意中间用 \(k\) 替代 \(top\),记录 root
for(int i=1;i<=n;i++){
int k=top;
while(k&&p[i]<p[st[k]]) k--;
if(k) rs[st[k]]=i;
else root=i;
if(k<top) ls[i]=st[k+1];
top=k; st[++top]=i;
}
CDQ分治
inline void cdq(int l,int r){
if(l>=r) return ;
int mid=(l+r)>>1;
cdq(l,mid); cdq(mid+1,r);
//别忘记 cmp2
sort(b+l,b+mid+1,cmp2);
sort(b+mid+1,b+r+1,cmp2);
register int j=l;
for(register int i=mid+1;i<=r;i++){
while(b[i].y>=b[j].y&&j<=mid){//第二维
update(b[j].z,b[j].cnt);//第三维
j++;
}
b[i].ans+=query(b[i].z);
}
for(register int i=l;i<j;i++) update(b[i].z,-b[i].cnt);
}
KDT
int build(int l,int r){
if(l>r) return 0;
int p=++tot,mid=(l+r)>>1;
l(p)=l; r(p)=r;
for(int i=0;i<maxd;i++) Max[p][i]=-inf,Min[p][i]=inf;
for(int i=l;i<=r;i++){
for(int j=0;j<maxd;j++){
Max[p][j]=max(Max[p][j],d[c[i]][j]);
Min[p][j]=min(Min[p][j],d[c[i]][j]);
}
}
int D=find(l,r);
nth_element(c+l,c+mid,c+r+1,cmp);
id(p)=c[mid];
lc(p)=build(l,mid-1);
rc(p)=build(mid+1,r);
return p;
}
long long maxdist(int u){
long long dx=max(abs(x0-Max[u][0]),abs(x0-Min[u][0]));
long long dy=max(abs(y0-Max[u][1]),abs(y0-Min[u][1]));
return dx*dx+dy*dy;
}
bool maybe(int u){ return u&&(q.size()<k0||maxdist(u)>=q.top().len); }
void dfs(int p){
long long Di=dis(x0,y0,d[id(p)][0],d[id(p)][1]);
if(q.size()<k0) q.push((node){id(p),Di});
else if((node){id(p),Di}<q.top()){
q.pop(); q.push((node){id(p),Di});
}
if(maxdist(lc(p))>maxdist(rc(p))){
if(maybe(lc(p))) dfs(lc(p));
if(maybe(rc(p))) dfs(rc(p));
}else{
if(maybe(rc(p))) dfs(rc(p));
if(maybe(lc(p))) dfs(lc(p));
}
}
边带权并查集
int find(int x){
if(fa[x]==x) return x;
int pa=find(fa[x]);
dep[x]+=dep[fa[x]];
return fa[x]=pa;
}
void merge(int x,int y){
fa[x]=y; dep[x]=size[y]; size[y]+=size[x];
}
可并堆
struct Edge{
int cnt,head[maxn],Next[maxn],to[maxn],s[maxn],top;
int allocate(){
if(top) return s[top--];
else return ++cnt;
}
void recycle(int u){
s[++top]=u;
}
void push(int u,int v){
int o=allocate();
Next[o]=head[u];
to[o]=v;
head[u]=o;
}
}E;
int root[maxn],fa[maxn],q[maxn<<1];
ll val[maxn],add[maxn],mul[maxn];
ll pushdown(int u){
if(mul[u]==1&&add[u]==0) return val[u];
for(int i=E.head[u];i;i=E.Next[i]){
int v=E.to[i];
mul[v]*=mul[u];
add[v]*=mul[u];
add[v]+=add[u];
}
val[u]*=mul[u]; val[u]+=add[u];
mul[u]=1; add[u]=0;
return val[u];
}
int merge(int u,int v){
if(!u||!v) return u+v;
pushdown(u); pushdown(v);
if(val[u]>val[v]) swap(u,v);
E.push(u,v); fa[v]=u;
return u;
}
int pop(int u){
int l=1,r=0; q[1]=0;
pushdown(u);
for(int i=E.head[u];i;i=E.Next[i]){
int v=E.to[i];
q[++r]=v;
E.recycle(i);
}
while(l+1<=r){
q[++r]=merge(q[l],q[l+1]);
l+=2;
}
return q[l];
}
树状数组倍增
inline int get(int x){
int l=log2(K),sum=0,Ans=0;
for(int i=l;i>=0;i--){
int k=(1<<i);
if(Ans+k<=K&&sum+c[Ans+k]<x) sum+=c[Ans+k],Ans+=k;
}
return Ans+1;
}
可持久化字典树
注意判断 if(v) 再连另一条边
struct Trie{
void insert(int bh,int len,int v,int u){
if(len<0){ latest[u]=bh; return ; }
int ch=pre[bh]>>len&1;
if(v) tree[u][ch^1]=tree[v][ch^1];
tree[u][ch]=++cnt;
insert(bh,len-1,tree[v][ch],tree[u][ch]);
latest[u]=max(latest[tree[u][0]],latest[tree[u][1]]);
}
};
fhq
void pushup(int p){ s(p)=s(ls(p))+s(rs(p))+1; }
int New(int val){
int p=++tot;
s(p)=1; ls(p)=rs(p)=0;
val(p)=val; r(p)=rand();
return p;
}
void split(int p,int k,int& x,int& y){
if(!p){ x=y=0; return ; }
if(val(p)<=k){
x=p;
split(rs(p),k,rs(p),y);
}else{
y=p;
split(ls(p),k,x,ls(p));
}
pushup(p);
}
int merge(int p,int q){
if(!p||!q) return p+q;
if(r(p)<r(q)){
ls(q)=merge(p,ls(q));
pushup(q);
return q;
}else{
rs(p)=merge(rs(p),q);
pushup(p);
return p;
}
}
void insert(int v){
int x=0,y=0;
split(root,v-1,x,y);
root=merge(merge(x,New(v)),y);
}
void del(int v){
int x=0,y=0,z=0;
split(root,v,x,z); split(x,v-1,x,y);
root=merge(merge(x,merge(ls(y),rs(y))),z);
}
int kth(int k){
int p=root;
while(1){
if(k<=s(ls(p))) p=ls(p);
else if(k==s(ls(p))+1) return val(p);
else k-=s(ls(p))+1,p=rs(p);
}
}
int pre(int v){
int p=root,ans=0;
while(1){
if(!p) return ans;
else if(v<=val(p)) p=ls(p);
else ans=val(p),p=rs(p);
}
}
int suf(int v){
int p=root,ans=0;
while(1){
if(!p) return ans;
else if(v>=val(p)) p=rs(p);
else ans=val(p),p=ls(p);
}
}
int rnk(int v){
int x=0,y=0,ans;
split(root,v-1,x,y); ans=s(x)+1;
root=merge(x,y); return ans;
}
其他
模拟退火
注意先构造一个较优的解
double t=3000,deta=0.997;
while(t>1e-15){
if(clock()/CLOCKS_PER_SEC>0.95){
printf("%.3lf %.3lf",Ax,Ay);
exit(0);
}
double x=ansx+(rand()*2-RAND_MAX)*t,y=ansy+(rand()*2-RAND_MAX)*t;
double E=calc(x,y);
double D1=E-answ,D2=E-Aw;
if(D1<0) ansx=x,ansy=y,answ=E;
if(D2<0) Ax=x,Ay=y,Aw=E;
if(exp(-D1/t)*RAND_MAX>rand()) ansx=x,ansy=y;
t*=deta;
}
double limit=CLOCKS_PER_SEC*0.995;
if(clock()>limit){
cout<<ans;
exit(0);
}
int x=random(n-1)+1,y=random(n-1)+1;
swap(d[x],d[y]);
long long A=calc();
if(ans>A) ans=A;
else if(exp((ans-A)/t)*RAND_MAX<rand()) swap(d[x],d[y]);
t*=kt;
字符串
KMP
Next[1]=0; int j=0;
for(int i=2;i<=m;i++){//每次进入时,j=Next[i-1]
while(j&&p[i]!=p[j+1]) j=Next[j];
if(p[i]==p[j+1]) j++;
Next[i]=j;
}
SAM
两倍数组,last=tot=1; !p 的时候父亲设置为 \(1\) 而非 \(0/p\)
void add(int c){
int p=last; int np=last=++tot;
len[np]=len[p]+1; sum[np]=1;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else{
int nq=++tot; memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q]; len[nq]=len[p]+1;
fa[q]=fa[np]=nq;
for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
最小表示
int i=1,j=2,k;
while(i<=n&&j<=n){
for(k=0;k<n&&a[i+k]==a[j+k];k++);
if(k==n) break;
if(a[i+k]>a[j+k]){
i=i+k+1;
if(i==j) i++;
}else{
j=j+k+1;
if(i==j) j++;
}
}
int ans=min(i,j);
ACAM
void insert(int v){
if(ms.find(string(p+1))!=ms.end()) return ;
int len=strlen(p+1),u=0;
for(int i=1;i<=len;i++){
int c=idx(p[i]);
if(!ch[u][c]) ch[u][c]=++tot;
u=ch[u][c];
}
ms[string(p+1)]=v;
val[u]=v;
}
void getfail(){
queue<int> q; f[0]=0;
for(int i=1;i<=26;i++){
int u=ch[0][i];
if(!u) continue;
f[u]=last[u]=0; q.push(u);
}
while(!q.empty()){
int r=q.front(); q.pop();
for(int i=1;i<=26;i++){
int u=ch[r][i];
if(!u){ ch[r][i]=ch[f[r]][i]; continue; }
q.push(u);
f[u]=ch[f[r]][i];
last[u]=val[f[u]]?f[u]:last[f[u]];
if(last[u]) deg[last[u]]++;
}
}
}