模板:数据结构

数据结构

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

线段树

空间优化的线段树,支持区间查询/增加/修改/合并,使用时仅需根据实际情况修改Nodepush_upmaintainask

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 uv,vu。多个询问边有序插入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;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值