数据结构
以下数据结构均采用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