数据结构
以下数据结构均采用ll作为值类型,应用时根据需求调整。
typedef long long ll;
const ll INF=1e9;//表示(值)正无穷,且两个正无穷相加不会溢出
const int NPOS=-1;//表示(下标)不存在
离散化
在vector基础上的离散化,使用push_back()向其中插值,init()排序并离散化,ask查询离散化之后的值,at/[]运算符查离散前的值。
struct Ranker:vector<ll>
{
void init()
{
sort(begin(),end()),resize(unique(begin(),end())-begin());
}
int ask(ll x)const
{
return lower_bound(begin(),end(),x)-begin();
}
};
并查集
struct UnionFindSet:vector<int>
{
int siz;//剩余连通块数量
UnionFindSet(int n):siz(n)
{
for(int i=0; i<n; ++i)push_back(i);
}
int fa(int u)//u所在连通块上根节点
{
return at(u)!=u?at(u)=fa(at(u)):u;
}
void merge(int u,int w)//归并w所在连通块到u所在连通块
{
if(w=fa(w),u=fa(u),w!=u)at(w)=u,--siz;
}
};
单调队列
typedef pair<int,ll> pil;
struct MonotoneQueue:deque<pil>
{
void push(pil p,int k)//first插入元素下标,second插入值,插入并维护一个值单调递增的单调队列且队尾队首下标差值小于k
{
while(!empty()&&back().second>=p.second)pop_back();
for(push_back(p); p.first-front().first>=k;)pop_front();
}
};
ST表
O ( n log n ) O(n\log n) O(nlogn)预处理, O ( 1 ) O(1) O(1)求静态区间最小值。
/*
//可选优化
#define log2(n) LOG2[n]
struct Log:vector<ll>
{
Log(int N,ll E):vector<ll>(N,-1)
{
for(int i=1; i<N; ++i)at(i)=at(i/E)+1;
}
} LOG2(N,2);
*/
struct SparseTable
{
vector<vector<ll> > f;
SparseTable(const vector<ll> &a):f(log2(a.size())+1,a)
{
for(int k=0; k+1<f.size(); ++k)
for(int i=0; i+(1<<k)<a.size(); ++i)
f[k+1][i]=min(f[k][i],f[k][i+(1<<k)]);
}
ll ask(int l,int r)
{
int k=log2(r-l+1);
return min(f[k][l],f[k][r+1-(1<<k)]);
}
};
树状数组
模板中Base是对应的基础版本,支持单点修改区间查询。
一维
struct Fenwick
{
struct BaseFenwick
{
vector<ll> v;
BaseFenwick(int last):v(last+1,0) {}
void add(int x,ll w)
{
for(; x<v.size(); x+=x&-x)v[x]+=w;
}
ll ask(int x)
{
ll ans=0;
for(; x; x-=x&-x)ans+=v[x];
return ans;
}
};
pair<BaseFenwick,BaseFenwick> p;
Fenwick(int last):p(last,last) {}
void add(int x,ll w)
{
p.first.add(x,w),p.second.add(x,x*w);
}
void add(int l,int r,ll w)
{
add(l,w),add(r+1,-w);
}
ll ask(int x)
{
return (x+1)*p.first.ask(x)-p.second.ask(x);
}
ll ask(int l,int r)
{
return ask(r)-ask(l-1);
}
};
二维
高维的数据结构只要每一维维护低一维的数据即可。其余数据结构亦同理。
struct Fenwick2
{
struct BaseFenwick2
{
vector<Fenwick> v;
BaseFenwick2(int r,int c):v(r+1,c) {}
void add(int x,int b,int t,ll w)
{
for(; x<v.size(); x+=x&-x)v[x].add(b,t,w);
}
ll ask(int x,int b,int t)
{
ll ans=0;
for(; x; x-=x&-x)ans+=v[x].ask(b,t);
return ans;
}
};
pair<BaseFenwick2,BaseFenwick2> p;
Fenwick2(int r,int c):p(BaseFenwick2(r,c),BaseFenwick2(r,c)) {}
void add(int x,int b,int t,ll w)
{
p.first.add(x,b,t,w),p.second.add(x,b,t,x*w);
}
void add(int l,int b,int r,int t,ll w)//(l,b)~(r,t)
{
add(l,b,t,w),add(r+1,b,t,-w);
}
ll ask(int x,int b,int t)
{
return (x+1)*p.first.ask(x,b,t)-p.second.ask(x,b,t);
}
ll ask(int l,int b,int r,int t)
{
return ask(r,b,t)-ask(l-1,b,t);
}
};
线段树
空间优化的线段树,支持区间查询/增加/修改/合并,使用时仅需根据实际情况修改Node
、push_up
、maintain
、ask
。
struct SegmentTree
{
struct Node
{
ll add,set,min,sum;
};
vector<Node> v;
int LAST,L,R;//序列右端点,操作序列左右端点
SegmentTree(int n):LAST(n),v(2*n+1) {}
void build(ll a[],int l,int r)//快速建树,调用build(a,1,n)
{
if(l<r)
{
int m=l+(r-l)/2;
build(a,l,m),build(a,m+1,r);
lv(l,r).set=INF,lv(l,r).add=0;//清除本节点标记
}
else lv(l,r).set=a[l],lv(l,r).add=0;//两个的和为a[l]即可,根据需要自行选择
maintain(l,r);
}
Node &lv(int l,int r)//[l,r]对应的树上节点
{
return v[l+r|l!=r];
}
void push_down(Node &lc,Node &rc,Node &fa)//将fa的set、add标记传到lc、rc
{
if(fa.set!=INF)
{
lc.set=rc.set=fa.set,fa.set=INF;
lc.add=rc.add=0;
}
lc.add+=fa.add;
rc.add+=fa.add;
fa.add=0;
}
void push_up(const Node &lc,const Node &rc,Node &fa)//将区间左右相连的lc、rc归并到fa
{
fa.min=min(lc.min,rc.min);
fa.sum=lc.sum+rc.sum;
}
void maintain(int l,int r)//维护[l,r]
{
if(l<r)
{
int m=l+(r-l)/2;
push_up(lv(l,m),lv(m+1,r),lv(l,r));
}
if(lv(l,r).set!=INF)
lv(l,r).sum=(r-l+1)*(lv(l,r).min=lv(l,r).set);
lv(l,r).min+=lv(l,r).add;
lv(l,r).sum+=lv(l,r).add*(r-l+1);
}
Node ask(int l,int r,ll val=0,bool out=1)//查询[L,R],val为查询路径累加的add标记,out为外部调用标记,下同
{
if(out)return L=l,R=r,ask(1,LAST,val,0);
if(lv(l,r).set!=INF)
v[0].sum=(min(R,r)-max(l,L)+1)*(v[0].min=val+lv(l,r).add+lv(l,r).set);
else if(L<=l&&r<=R)
v[0].min=lv(l,r).min+val,v[0].sum=lv(l,r).sum+val*(r-l+1);
else
{
int m=l+(r-l)/2;
if(R<=m)return ask(l,m,lv(l,r).add+val,0);
if(L>m)return ask(m+1,r,lv(l,r).add+val,0);
push_up(ask(l,m,lv(l,r).add+val,0),ask(m+1,r,lv(l,r).add+val,0),v[0]);
}
return v[0];
}
void add(int l,int r,ll val,bool out=1)//[L,R]加上val
{
if(out)return L=l,R=r,add(1,LAST,val,0);
if(L<=l&&r<=R)lv(l,r).add+=val;
else
{
int m=l+(r-l)/2;
push_down(lv(l,m),lv(m+1,r),lv(l,r));
if(L<=m)add(l,m,val,0);
else maintain(l,m);
if(R>m)add(m+1,r,val,0);
else maintain(m+1,r);
}
maintain(l,r);
}
void set(int l,int r,ll val,bool out=1)//[L,R]设为val
{
if(out)return L=l,R=r,set(1,LAST,val,0);
if(L<=l&&r<=R)lv(l,r).set=val,lv(l,r).add=0;
else
{
int m=l+(r-l)/2;
push_down(lv(l,m),lv(m+1,r),lv(l,r));
if(L<=m)set(l,m,val,0);
else maintain(l,m);
if(R>m)set(m+1,r,val,0);
else maintain(m+1,r);
}
maintain(l,r);
}
};
主席树
求区间第k小,可能需要离散化。
struct HJTree
{
struct Node
{
int ch[2],sum;
} v[MAXN<<5];
int m,siz,tot,T[MAXN];//siz前缀树的个数,tot所有顶点的个数
void build(int a[],int n,int m)//用a[1]~a[n]建树,a[i]的值不超过m
{
for(this->m=m,siz=tot=1; siz<=n; ++siz)
ins(&(T[siz]=T[siz-1]),a[siz]);
}
void ins(int *rt,int pos,int val=1)
{
for(int l=1,r=m;;)
{
v[tot]=v[*rt],v[*rt=tot++].sum+=val;
if(l==r)break;
int m=l+(r-l)/2;
if(pos>m)rt=&v[*rt].ch[1],l=m+1;
else rt=&v[*rt].ch[0],r=m;
}
}
int ask(int x,int y,int k)
{
for(int kx=T[x-1],ky=T[y],l=1,r=m,LR;;)
{
if(l>=r)return l;
int d=v[v[ky].ch[0]].sum-v[v[kx].ch[0]].sum,m=l+(r-l)/2;
if(k>d)k-=d,l=m+1,LR=1;
else r=m,LR=0;
kx=v[kx].ch[LR],ky=v[ky].ch[LR];
}
}
};
动态主席树
struct HJTree
{
int m,siz,tot,T[MAXN],S[MAXN],use[MAXN];
struct Node
{
int ch[2],sum;
} v[MAXN<<5];
void build(int a[],int n,int m)//用a[1]~a[n]建树,a[i]的值不超过m
{
for(this->m=m,siz=tot=1; siz<=n; ++siz)
ins(&(T[siz]=T[siz-1]),a[i]),s[siz]=0;
}
void ins(int *rt,int pos,int val=1)//和静态主席树一样
{
for(int l=1,r=m;;)
{
v[tot]=v[*rt],v[*rt=tot++].sum+=val;
if(l==r)break;
int m=l+(r-l)/2;
if(pos>m)rt=&v[*rt].ch[1],l=m+1;
else rt=&v[*rt].ch[0],r=m;
}
}
void add(int x,int pos,int val)
{
for(; x<siz; x+=x&-x)
ins(&S[x],pos,val);
}
int sum(int x)
{
int ret=0;
for(; x; x-=x&-x)
ret+=v[v[use[x]].ch[0]].sum;
return ret;
}
int ask(int x,int y,int k)
{
for(int i=--x; i; i-=i&-i)use[i]=S[i];
for(int i=y; i; i-=i&-i)use[i]=S[i];
for(int kx=T[x],ky=T[y],l=1,r=m,LR;;)
{
if(l>=r)return l;
int m=l+(r-l)/2,d=sum(y)-sum(x)+v[v[ky].ch[0]].sum-v[v[kx].ch[0]].sum;
if(k>d)k-=d,l=m+1,LR=1;
else r=m,LR=0;
kx=v[kx].ch[LR],ky=v[ky].ch[LR];
for(int i=x; i; i-=i&-i)use[i]=v[use[i]].ch[LR];
for(int i=y; i; i-=i&-i)use[i]=v[use[i]].ch[LR];
}
}
};
无旋Treap
按子树大小分裂
struct FhqTreap
{
struct Node
{
int ch[2],siz,rev;
ll key,val,min,add;
void REV()
{
rev^=1,swap(ch[0],ch[1]);
}
void ADD(ll v)
{
val+=v,min+=v,add+=v;
}
};
vector<Node> v;
int root;
FhqTreap():v(1),root(0) {}
void push_down(int k)
{
if(!k)return;
for(int i=0,*ch=v[k].ch; i<2; ++i)
if(ch[i])
{
v[ch[i]].ADD(v[k].add);
if(v[k].rev)v[ch[i]].REV();
}
v[k].add=v[k].rev=0;
}
void push_up(int k)
{
if(!k)return;
v[k].siz=1,v[k].min=v[k].val;
for(int i=0,*ch=v[k].ch; i<2; ++i)
if(ch[i])
v[k].siz+=v[ch[i]].siz,v[k].min=min(v[k].min,v[ch[i]].min);
}
int merge(int a,int b)
{
if(!a||!b)return a+b;
if(v[a].key<v[b].key)
return push_down(a),v[a].ch[1]=merge(v[a].ch[1],b),push_up(a),a;
return push_down(b),v[b].ch[0]=merge(a,v[b].ch[0]),push_up(b),b;
}
void split(int a,int s,int &l,int &r)
{
if(!s)l=0,r=a;
else if(v[v[a].ch[0]].siz<s)
push_down(a),split(v[a].ch[1],s-v[v[a].ch[0]].siz-1,v[a].ch[1],r),push_up(l=a);
else
push_down(a),split(v[a].ch[0],s,l,v[a].ch[0]),push_up(r=a);
}
void push_back(ll d)
{
v.push_back(Node {{0,0},1,0,rand(),d,d,d});
root=merge(root,v.size()-1);
}
void insert(int x,ll d)
{
v.push_back(Node {{0,0},1,0,rand(),d,d,d});
int a,b,c;
split(root,x-1,a,b);
root=merge(merge(a,v.size()-1),b);
}
void erase(int x)
{
int a,b,c;
split(root,x,a,b),split(a,x-1,a,c),root=merge(a,b);
}
void add(int l,int r,ll d)
{
int a,b,c;
split(root,r,b,c),split(b,l-1,a,b),v[b].ADD(d),root=merge(merge(a,b),c);
}
Node ask(int l,int r)
{
int a,b,c;
split(root,r,b,c),split(b,l-1,a,b);
Node ret=v[b];
return root=merge(merge(a,b),c),ret;
}
void reverse(int l,int r)
{
int a,b,c;
split(root,r,b,c),split(b,l-1,a,b),v[b].REV(),root=merge(merge(a,b),c);
}
void revolve(int l,int r,int d)
{
int a,b,c,e=r-l+1;
split(root,r,b,c),split(b,l-1,a,b),split(b,(e-d%e)%e,b,e);
root=merge(merge(a,merge(e,b)),c);
}
};
按值大小分裂,即排序树
struct FhqTreap
{
struct Node
{
int ch[2],siz;
ll key,val;
};
vector<Node> v;
int root;
FhqTreap():v(1),root(0) {}
void push_up(int k)
{
v[k].siz=v[v[k].ch[0]].siz+v[v[k].ch[1]].siz+1;
}
int merge(int a,int b)
{
if(!a||!b)return a+b;
if(v[a].key<v[b].key)
return v[a].ch[1]=merge(v[a].ch[1],b),push_up(a),a;
return v[b].ch[0]=merge(a,v[b].ch[0]),push_up(b),b;
}
void splitVal(int a,ll w,int &l,int &r)//按值将树划分,使得左子树上的值恰小于w
{
if(!a)l=r=0;
else if(v[a].val>w)splitVal(v[a].ch[0],w,l,v[a].ch[0]),push_up(r=a);
else splitVal(v[a].ch[1],w,v[a].ch[1],r),push_up(l=a);
}
void insert(ll x)
{
int a,b;
v.push_back(Node {{0,0},1,rand(),x});
splitVal(root,x,a,b),root=merge(merge(a,v.size()-1),b);
}
void erase(ll x)
{
int a,b,c;
splitVal(root,x,a,b),splitVal(a,x-1,a,c);
root=merge(merge(a,merge(v[c].ch[0],v[c].ch[1])),b);
}
ll kth(int k)
{
for(int u=root,ls;;)
{
if(ls=v[v[u].ch[0]].siz,ls+1==k)
return v[u].val;
if(ls<k)k-=ls+1,u=v[u].ch[1];
else u=v[u].ch[0];
}
}
int lower_bound(ll x)
{
return upper_bound(x-1);
}
int upper_bound(ll x)
{
int a,b,ret;
return splitVal(root,x,a,b),ret=v[a].siz+1,root=merge(a,b),ret;
}
};
珂朵莉树
Willem, Chtholly and Seniorious
区间加减、第k大、k次方和。数据随机。
#include<bits/stdc++.h>
#define mul(a,b,c) ((a)*(b)%(c))
using namespace std;
typedef long long ll;
typedef pair<int,ll> pil;
ll pow(ll a,ll b,ll m)
{
ll r=1;
for(a%=m; b; a=mul(a,a,m),b>>=1)
if(b&1)r=mul(r,a,m);
return r;
}
struct ChthollyTree:map<int,pil>
{
iterator split(int pos)
{
iterator it=lower_bound(pos);
if(it!=end()&&it->first==pos)return it;
--it;
if(pos>it->second.first)return end();
pair<int,pil> p=*it;
erase(it);
insert(make_pair(p.first,pil(pos-1,p.second.second)));
return insert(make_pair(pos,p.second)).first;
}
void add(int l,int r,ll val)
{
for(iterator b=split(l),e=split(r+1); b!=e; ++b)b->second.second+=val;
}
void set(int l,int r,ll val)
{
erase(split(l),split(r+1)),insert(make_pair(l,pil(r,val)));
}
ll rank(int l,int r,int k)
{
vector<pair<ll,int> > v;
for(iterator b=split(l),e=split(r+1); b!=e; ++b)
v.push_back(make_pair(b->second.second,b->second.first-b->first+1));
sort(v.begin(),v.end());
for(int i=0; i<v.size(); ++i)
if(k-=v[i].second,k<=0)return v[i].first;
return -1;
}
ll sum(int l,int r,ll ex,ll m)
{
ll res=0;
for(iterator b=split(l),e=split(r+1); b!=e; ++b)
res=(res+mul(b->second.first-b->first+1,pow(b->second.second,ex,m),m))%m;
return res;
}
} t;
ll n,m,seed,vmax,M=1e9+7;
ll rnd()
{
ll ret=seed;
seed=(seed*7+13)%M;
return ret;
}
int main()
{
scanf("%lld%lld%lld%lld",&n,&m,&seed,&vmax);
for(int i=1; i<=n; ++i)
t.insert(make_pair(i,pil(i,rnd()%vmax+1)));
for(int i=1; i<=m; ++i)
{
int op=rnd()%4+1,l=rnd()%n+1,r=rnd()%n+1;
if(l>r)swap(l,r);
ll x=rnd()%(op==3?r-l+1:vmax)+1;
if(op==1)t.add(l,r,x);
if(op==2)t.set(l,r,x);
if(op==3)printf("%lld\n",t.rank(l,r,x));
if(op==4)printf("%lld\n",t.sum(l,r,x,rnd()%vmax+1));
}
}
脑洞治疗仪
把一段连续区间的01移动到到另一段区间上,求在某个区间中最大的连续0序列有多大。
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef pair<int,ll> pil;
struct ChthollyTree:map<int,pil>
{
iterator split(int pos)
{
iterator it=lower_bound(pos);
if(it!=end()&&it->first==pos)return it;
--it;
if(pos>it->second.first)return end();
pair<int,pil> p=*it;
erase(it);
insert(make_pair(p.first,pil(pos-1,p.second.second)));
return insert(make_pair(pos,p.second)).first;
}
void set(int l,int r,ll val)
{
erase(split(l),split(r+1)),insert(make_pair(l,pil(r,val)));
}
void scure(int l,int r,int x,int y)
{
iterator e=split(r+1),b=split(l),it=b;
int sum=0;
for(; b!=e; ++b)
if(b->second.second)
sum+=b->second.first-b->first+1;
erase(it,e);
insert(make_pair(l,pil(r,0)));
if(!sum)return;
e=split(y+1),b=split(x),it=b;
if(sum>=y-x+1)
{
erase(b,e);
insert(make_pair(x,pil(y,1)));
return;
}
for( ; b!=e; ++b)
if(!b->second.second)
{
sum-=b->second.first-b->first+1;
if(sum<0)return set(b->first,b->second.first+sum,1);
b->second.second=1;
}
}
ll MAX(int l,int r)
{
iterator e=split(r+1),b=split(l);
ll res=0,now=0;
for(; b!=e; ++b)
if(!b->second.second)
now+=b->second.first-b->first+1;
else if(now)res=max(res,now),now=0;
return max(res,now);
}
} t;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
t.insert(make_pair(1,pil(n,1)));
for(int op,l,r,x,y; m--;)
{
scanf("%d%d%d",&op,&l,&r);
if(op==0)t.set(l,r,0);
else if(op==1)scanf("%d%d",&x,&y),t.scure(l,r,x,y);
else printf("%d\n",t.MAX(l,r));
}
}
莫队
普通莫队
struct Mo
{
struct Query
{
int l,r,id;
bool operator<(const Query& n)const
{
return l/BS!=n.l/BS?l<n.l:r<n.r;
}
};
vector<Query> q;
int L,R;
void query(int l,int r)
{
q.push_back(Query {l,r,q.size()});
}
void rev(int x) {}
void cal(int id) {}
void ask()
{
L=0,R=-1;
sort(q.begin(),q.end());
for(int i=0; i<q.size(); ++i)
{
while(L<q[i].l)rev(L++);
while(L>q[i].l)rev(--L);
while(R<q[i].r)rev(++R);
while(R>q[i].r)rev(R--);
cal(q[i].id);
}
}
};
动态莫队
struct Mo
{
struct Update
{
int pos,NEW,OLD;
};
struct Query
{
int t,l,r,id;
bool operator<(const Query& n)const
{
return l/BS!=n.l/BS?l<n.l:
r/BS!=n.r/BS?r<n.r:t<n.t;
}
};
vector<Update> cq;
vector<Query> q;
int T,L,R;
Mo():cq(1) {}
void query(int x,int y)
{
q.push_back(Query {cq.size()-1,x,y,q.size()});
}
void update(int x,int y)
{
cq.push_back(Update {x,y,t[x]}),t[x]=y;
}
void set(int x,int d)
{
if(vis[x])return rev(x),a[x]=d,rev(x);
a[x]=d;
}
void rev(int x) {}
void cal(int id) {}
void ask()
{
T=L=0,R=-1;
sort(q.begin(),q.end());
for(int i=0; i<q.size(); ++i)
{
while(T<q[i].t)++T,set(cq[T].pos,cq[T].NEW);
while(T>q[i].t)set(cq[T].pos,cq[T].OLD),--T;
while(L<q[i].l)rev(L++);
while(L>q[i].l)rev(--L);
while(R<q[i].r)rev(++R);
while(R>q[i].r)rev(R--);
cal(q[i].id);
}
}
};
树上莫队
按照欧拉序分块,使用Tarjan在生成欧拉序的同时预处理所有询问的lca,预处理时间复杂度
O
(
n
+
q
)
O(n+q)
O(n+q)。
h为查询图,即如果有一个询问(u,v),即在h上连
u
→
v
,
v
→
u
u\to v,v\to u
u→v,v→u。多个询问边有序插入h。
struct TreeMo:Graph
{
struct Query
{
int l,r,lca,id;
bool operator<(const Query &b)const
{
return l/BS!=b.l/BS?l<b.l:r<b.r;
}
};
vector<Query> q;
vector<int> dfp,dfi,dfo;
UnionFindSet ufs;
Graph h;
int L,R;
TreeMo(int n):Graph(n),h(n),dfp(n*2+1),dfi(n),dfo(n),ufs(n) {}
void query(int x,int y)
{
h.add(Edge {x,y}),h.add(Edge {y,x});
q.push_back(Query {0,0,0,q.size()});
}
void rev(int x) {}
void cal(int id) {}
void dfs(int u,int &cnt)
{
dfp[dfi[u]=++cnt]=u;
for(int i=0,k,to; i<v[u].a.size(); ++i)
if(k=v[u].a[i],to=e[k].to,!dfi[to])
dfs(to,cnt),ufs.merge(u,to);
dfp[dfo[u]=++cnt]=u;
for(int i=0,k,to,id; i<h.v[u].a.size(); ++i)
if(k=h.v[u].a[i],id=k/2,to=h.e[k].to,dfo[to])
{
q[id].lca=ufs.fa(to);
q[id].l=q[id].lca!=u?dfo[u]:dfi[u];
q[id].r=dfi[to];
}
}
void ask(int root=1)
{
dfs(root,BS=0),BS=sqrt(BS);
sort(q.begin(),q.end());
L=0,R=-1;
for(int i=0; i<q.size(); ++i)
{
while(L<q[i].l)rev(dfp[L++]);
while(L>q[i].l)rev(dfp[--L]);
while(R<q[i].r)rev(dfp[++R]);
while(R>q[i].r)rev(dfp[R--]);
if(q[i].lca!=dfp[L])rev(q[i].lca);
cal(q[i].id);
if(q[i].lca!=dfp[L])rev(q[i].lca);
}
}
};
动态树上莫队
struct CapitalTreeMo:Graph
{
struct Update
{
int pos,NEW,OLD;
};
struct Query
{
int t,l,r,lca,id;
bool operator<(const Query &b)const
{
return l/BS!=b.l/BS?l<b.l:
r/BS!=b.r/BS?r<b.r:t<b.t;//在BZOJ4129上去掉r/BS还快100ms?
}
};
vector<Update> cq;
vector<Query> q;
vector<int> dfp,dfi,dfo;
UnionFindSet ufs;
Graph h;
int T,L,R;
CapitalTreeMo(int n):cq(1),Graph(n),h(n),dfp(n*2+1),dfi(n),dfo(n),ufs(n) {}
void query(int x,int y)
{
h.add(Edge {x,y}),h.add(Edge {y,x});
q.push_back(Query {cq.size()-1,0,0,0,q.size()});
}
void update(int x,int y)
{
cq.push_back(Update {x,y,t[x]}),t[x]=y;
}
void dfs(int u,int &cnt)
{
dfp[dfi[u]=++cnt]=u;
for(int i=0,k,to; i<v[u].a.size(); ++i)
if(k=v[u].a[i],to=e[k].to,!dfi[to])
dfs(to,cnt),ufs.merge(u,to);
dfp[dfo[u]=++cnt]=u;
for(int i=0,k,to,id; i<h.v[u].a.size(); ++i)
if(k=h.v[u].a[i],id=k/2,to=h.e[k].to,dfo[to])
{
q[id].lca=ufs.fa(to);
q[id].l=q[id].lca!=u?dfo[u]:dfi[u];
q[id].r=dfi[to];
}
}
void set(int u,int d)
{
if(vis[u])return rev(u),a[u]=d,rev(u);
a[u]=d;
}
void rev(int u) {}
void cal(int id) {}
void ask(int root=1)
{
dfs(root,BS=0),BS=sqrt(BS);
sort(q.begin(),q.end());
T=L=0,R=-1;
for(int i=0; i<q.size(); ++i)
{
while(T<q[i].t)++T,set(cq[T].pos,cq[T].NEW);
while(T>q[i].t)set(cq[T].pos,cq[T].OLD),--T;
while(L<q[i].l)rev(dfp[L++]);
while(L>q[i].l)rev(dfp[--L]);
while(R<q[i].r)rev(dfp[++R]);
while(R>q[i].r)rev(dfp[R--]);
if(q[i].lca!=dfp[L])rev(q[i].lca);
cal(q[i].id);
if(q[i].lca!=dfp[L])rev(q[i].lca);
}
}
};
匹配
KMP
struct KMP
{
const string s;
vector<int> next;
KMP(const string &s):s(s),next(s.size()+1,0)
{
for(int i=1,j; i<s.size(); ++i)
{
for(j=next[i]; j&&s[i]!=s[j]; j=next[j]);
next[i+1]=s[i]==s[j]?j+1:0;
}
}
bool find_in(const string &t)
{
for(int i=0,j=0; i<t.size(); ++i)
{
for(; j&&s[j]!=t[i]; j=next[j]);
if(s[j]==t[i])++j;
if(j==s.size())return 1;//不return可得到t中s的所有匹配地址i+1-s.size()
}
return 0;
}
};
AC自动机
ask之前需要调用getFail()
生成失配函数。
struct AhoCorasick
{
struct Node
{
int ch[26],val,f,last;
int &to(char c)
{
return ch[c-'a'];
}//如果不确定c的范围,使用map
};
vector<Node> v;
AhoCorasick():v(1) {}
void getFail()
{
for(deque<int> q(1,v[0].last=v[0].f=0); !q.empty(); q.pop_front())
for(char c='a'; c<='z'; ++c)
{
int r=q.front(),u=v[r].to(c),w=v[r].f;
if(!r&&u)
{
q.push_back(u);
v[u].f=v[u].last=0;
continue;
}
if(!u)
{
v[r].to(c)=v[w].to(c);
continue;
}
q.push_back(u);
while(w&&!v[w].to(c))w=v[w].f;
v[u].f=v[w].to(c);
v[u].last=v[v[u].f].val?v[u].f:
v[v[u].f].last;
}
}
void add(const string &s,int val,int u=0)
{
for(int i=0; i<s.size(); u=v[u].to(s[i++]))
if(!v[u].to(s[i]))
{
v[u].to(s[i])=v.size();
v.push_back(Node());
}
v[u].val=val;
}
bool find_in(const string &s,int u=0)
{
for(int i=0; i<s.size(); ++i)
if(u=v[u].to(s[i]),
v[u].val||v[u].last)
return 1;
return 0;
}
};
后缀数组
m:字符集大小。
s:字符串,其中最后一位为加入的0。
sa[i]:字典序第i小的是哪个后缀。
rk[i]:后缀i的排名。
h[i]:lcp(sa[i],sa[i−1])。
struct SufArr
{
vector<int> sa,rk,h;
SufArr(const vector<int> &s,int m):sa(s.size(),0),rk(s),h(s.size(),0)
{
vector<int> cnt(s.size()+m,0);
for(int i=0; i<s.size(); ++i)++cnt[rk[i]];
for(int i=1; i<m; ++i)cnt[i]+=cnt[i-1];
for(int i=0; i<s.size(); ++i)sa[--cnt[rk[i]]]=i;
for(int k=1,j=0; k<=s.size()&&j<s.size()-1; k<<=1)
{
for(int i=0; i<s.size(); ++i)
{
if(j=sa[i]-k,j<0)j+=s.size();
h[cnt[rk[j]]++]=j;
}
cnt[0]=sa[h[0]]=j=0;
for(int i=1; i<s.size(); ++i)
{
if(rk[h[i]]!=rk[h[i-1]]||rk[h[i]+k]!=rk[h[i-1]+k])
cnt[++j]=i;
sa[h[i]]=j;
}
swap(rk,sa),swap(sa,h);
}
for(int i=0,k=0,j=rk[0]; i<s.size()-1; ++i,++k)
for(; ~k&&s[i]!=s[sa[j-1]+k]; j=rk[sa[j]+1],--k)
h[j]=k;
}
};
暴力回文
时间复杂度
O
(
n
2
)
O(n^2)
O(n2),常数低,但会被ababababa
这样的数据卡。
int palindrome(const char *s)
{
int ans=0;
for(int i=0,b,e; s[i]; ++i)
{
for(b=i; s[i]==s[i+1];)++i;
for(e=i+1; b&&s[b-1]==s[e];)--b,++e;
if(ans<e-b)ans=e-b;//此时[b,e)为最大回文区间
}
return ans;
}
Manacher线性回文
对于一个位置i,[i−f[i]+1,i+f[i]−1]是最长的以i为中心的奇回文串,g[i]−i是最长的以i为开头的回文串长度。
struct Manacher
{
vector<int> t,f,g;
Manacher(const string &s):t(s.size()+1<<1,0),f(t),g(t)//t初始值为s中没有出现过的值,g开始为0
{
for(int i=0; i<s.size(); ++i)t[i+1<<1]=s[i];
for(int i=1,p=0,m=0; i<t.size(); ++i)
{
for(f[i]=i<m?min(f[2*p-i],m-i):1;
0<i-f[i]&&i+f[i]<t.size()&&
t[i-f[i]]==t[i+f[i]];)
++f[i];
if(m<i+f[i])m=i+f[p=i];
}
for(int i=2; i<t.size(); ++i)
if(g[i-f[i]+1]<i+1)g[i-f[i]+1]=i+1;
for(int i=1; i<t.size(); ++i)
if(g[i]<g[i-1])g[i]=g[i-1];
}
int ask(int l,int r)//多次询问可做一个ST表
{
int ans=0;
for(int i=l+1<<1,e=r+1<<1; i<=e; i+=2)
if(ans<g[i]-i)ans=g[i]-i;
return ans;
}
};