C++竞赛常用实用代码(4)

前言

因为后面几个板子都是跟字符串相关的,笔者就懒得写进“C++竞赛常用实用代码(5)”了,直接放一块,所以这篇的代码量可能是最大的。

简要解释一下为什么我代码中绝大部分结构体命名是“itn”,这是因为早期学写代码时经常把“int”错打为“itn”,所以在写结构体时帮忙区分。

所有代码中未加的默认加上“#define ll long long”

上一页:C++竞赛常用实用代码(3)

目录

拓扑排序判环

树链剖分

非旋treap模板

有理数(分数防卡精)结构体

求树的重心

Prim求最小生成树(多用于完全图)

李超树板子(动态开点)

线段树合并

快速沃尔什变换

KMP板子

EXKMP板子

Manacher板子

AC自动机板子

后缀数组sa+height

后缀自动机

回文自动机


下一页:C++竞赛常用实用代码(5)

拓扑排序判环

inline bool check(){
	queue<int>q;int t=0;
	int dr[n+5];
	for(int i=1;i<=n;i++)dr[i]=d[i];//d[i]为第i个点的入度,此处不能破坏原数组,所以copy到dr
	for(int i=1;i<=n;i++)if(!dr[i])q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop(),t++;
		for(int i=0;i<G[u].size();i++)
			if(dr[G[u][i].a]>0){
				int v=G[u][i].a;dr[v]--;
				if(!dr[v])q.push(v);
			}
	}
	return t>=n;
}

树链剖分

int n=read(),m,tr[MAXN*3],p,IN;//zkw线段树维护 
int d[MAXN],hson[MAXN],tp[MAXN],id[MAXN],fa[MAXN];
vector<int>G[MAXN];
inline int dfs1(int x){
	int siz=1,hs=0;d[x]=d[fa[x]]+1;
	for(int i=0;i<G[x].size();i++)
		if(G[x][i]!=fa[x]){fa[G[x][i]]=x;
			int si=dfs1(G[x][i]);siz+=si;
			if(si>hs)hs=si,hson[x]=G[x][i];
		}
	return siz;
}
inline void dfs2(int x){
	id[x]=++IN;
	if(x==hson[fa[x]])tp[x]=tp[fa[x]];
	else tp[x]=x;
	if(hson[x]>0)dfs2(hson[x]);
	for(int i=0;i<G[x].size();i++)
	if(G[x][i]!=fa[x]&&G[x][i]!=hson[x])dfs2(G[x][i]);
}
inline int lca(int u,int v){
	while(tp[u]!=tp[v]){
		if(d[tp[u]]>d[tp[v]])u=fa[tp[u]];
		else v=fa[tp[v]];
	}
	return d[u]>d[v]?v:u;
}
//其它的都是线段树的板子,不想水了

非旋treap模板

struct node{
	int x,y;
	node(){}
	node(int X,int Y){x=X,y=Y;}
};
inline void updata(int x){siz[x]=siz[ls[x]]+siz[rs[x]]+1;}//维护siz
inline void exc(int x){//下传反转懒标记
	if(lazy[x]){lazy[ls[x]]^=1,lazy[rs[x]]^=1,lazy[x]=0,swap(ls[x],rs[x]);}
}
inline node split(int x,int k){//分裂操作
	if(!x||!k)return node(0,x);
	exc(x),fa[x]=0;node res;
	if(siz[ls[x]]>=k)res=split(ls[x],k),ls[x]=res.y,fa[res.y]=x,updata(x),res.y=x;
	else res=split(rs[x],k-siz[ls[x]]-1),rs[x]=res.x,fa[res.x]=x,updata(x),res.x=x;
	return res;
}
inline int mergg(int x,int y){//合并操作
	if(!x||!y)return x^y+(fa[x^y]=0);
	exc(x),exc(y);int res;
	if(val[x]<val[y])rs[x]=mergg(rs[x],y),fa[rs[x]]=x,updata(x),res=x;
	else ls[y]=mergg(x,ls[y]),fa[ls[y]]=y,updata(y),res=y;
	return res+(fa[res]=0);
}
//有了分裂和合并,其它操作有手就能打,这里就不用水了

有理数(分数防卡精)结构体

#define ll long long
inline ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
struct Q{
	ll z,m;
	Q(){m=1;}
	Q(ll U,ll V){z=U,m=V;}
	Q exc(){//求倒数
		Q res;res.z=m,res.m=z;
		if(res.m<0)res.z=-res.z,res.m=-res.m;
		return res;
	}
	double dec(){return double(z)/m;}//转换成双精度浮点数
	Q operator*(const Q&c){
		Q res=c;ll g;
		res.z*=z,res.m*=m;
		if(res.m<0)res.z=-res.z,res.m=-res.m;
		g=max(gcd(res.m,res.z>0?res.z:-res.z),1ll);
		res.z/=g,res.m/=g;
		return res;
	}
	Q operator+(const Q&c){
		Q res;ll g;
		res.m=m*c.m,res.z=z*c.m+m*c.z;
		if(res.m<0)res.z=-res.z,res.m=-res.m;
		g=max(gcd(res.m,res.z>0?res.z:-res.z),1ll);
		res.z/=g,res.m/=g;
		return res;
	}
};

求树的重心

int ct,ct_,si=0x3f3f3f3f;//ct_是树的另一个重心(如果有的话)
vector<int>G[MAXN];
inline int dfs(int x,int fa){
	int mx=0,nu=1;
	for(int i=0;i<G[x].size();i++)
		if(G[x][i]!=fa){
			int v=G[x][i],nm=dfs(v,x);
			nu+=nm,mx=max(mx,nm);
		}
	mx=max(mx,n-nu);
	if(mx<si)ct=x,ct_=0,si=mx;
	else if(mx==si)ct_=x;
	return nu;
}

Prim求最小生成树(多用于完全图)

inline int Prim(){ //邻接矩阵
    int an=0; //生成树边的总长
    memset(v,0,sizeof(v)),v[1]=1;
    for(int i=1;i<=n;i++)w[i]=c[1][i]; //连通块连向其他点的边权
    for(int K=1;K<n;K++){
    	int u=1,ad=0x3f3f3f3f;
	    for(int i=1;i<=n;i++)if(!v[i]&&w[i]<ad)ad=w[i],u=i;
    	an+=ad,v[u]=1;
    	for(int i=1;i<=n;i++)w[i]=min(w[i],c[u][i]);
    }
    return an;
}

李超树板子(动态开点)

#define INF 0x7f7f7f7f
#define ll long long
int IN;
struct lcs{
	int ls,rs;ll k,b;lcs(){}
	lcs(ll K,ll B){ls=rs=0,k=K,b=B;}
}t[MAXN<<4];
inline void add(int x,ll l,ll r,ll k,ll b){//插入一条y=kx+b的直线
	if(x==0)return;
	ll tk=t[x].k,tb=t[x].b,mid=(l+r)>>1;
	if(l*k+b>=l*tk+tb&&r*k+b>=r*tk+tb){
		t[x].k=k,t[x].b=b;return;
	}
	else if(l*k+b<l*tk+tb&&r*k+b<r*tk+tb)return;
	else{
		if(!t[x].ls)t[x].ls=++IN,t[IN]=lcs(tk,tb);
		else add(t[x].ls,l,mid,tk,tb);
		if(!t[x].rs)t[x].rs=++IN,t[IN]=lcs(tk,tb);
		else add(t[x].rs,mid+1,r,tk,tb);
		t[x].k=k,t[x].b=b;
	}
}
inline ll sch(int x,ll l,ll r,ll g){//查找x=g处的最高点
	if(x==0)return -INF;
	ll mid=(l+r)>>1,res=g*t[x].k+t[x].b;
	if(g<=mid)res=max(res,sch(t[x].ls,l,mid,g));
	else res=max(res,sch(t[x].rs,mid+1,r,g));
	return res;
}

线段树合并

直接以动态开点李超树为例(相关函数如上)

inline int mergg(int x,int y,ll l,ll r){
	if(!x||!y)return x|y;
	ll mid=(l+r)>>1;
	t[x].ls=mergg(t[x].ls,t[y].ls,l,mid),t[y].ls=0;
	t[x].rs=mergg(t[x].rs,t[y].rs,mid+1,r),t[y].rs=0;
	add(x,l,r,t[y].k,t[y].b);
	return x;
}

快速沃尔什变换FWT

转自讲解:https://blog.csdn.net/weixin_43960414/article/details/106668931

//qm(x,y) 表示 x % y , zxy 是模数
inline void DWTOR(int *s,int m) {//按位或
	for(int k = m;k > 1;k >>= 1) {
		for(int i = 0;i < m;i += k) {
			for(int j = i+(k>>1);j < i+k;j ++) {
				int s0 = s[j-(k>>1)],s1 = s[j];
				s[j] = qm((s0 +0ll+ s1) , zxy);
			}
		}
	}
	return ;
}
inline void IDWTOR(int *s,int m) {//按位或(逆变换)
	for(int k = 2;k <= m;k <<= 1) {
		for(int i = 0;i < m;i += k) {
			for(int j = i+(k>>1);j < i+k;j ++) {
				int s0 = s[j-(k>>1)],s1 = s[j];
				s[j] = qm((s1 +0ll+ zxy - s0) , zxy);
			}
		}
	}
	return ;
}
 
 
inline void DWTAND(int *s,int m) {//按位与
	for(int k = m;k > 1;k >>= 1) {
		for(int i = 0;i < m;i += k) {
			for(int j = i+(k>>1);j < i+k;j ++) {
				LL s0 = s[j-(k>>1)],s1 = s[j];
				s[j-(k>>1)] = qm((s0 +0ll+ s1) , zxy);
			}
		}
	}
	return ;
}
inline void IDWTAND(int *s,int m) {//按位与(逆变换)
	for(int k = 2;k <= m;k <<= 1) {
		for(int i = 0;i < m;i += k) {
			for(int j = i+(k>>1);j < i+k;j ++) {
				int s0 = s[j-(k>>1)],s1 = s[j];
				s[j-(k>>1)] = qm((s0 +0ll+ zxy - s1) , zxy);
			}
		}
	}
	return ;
}
 
 
inline void DWTXOR(int *s,int m) {//异或
	for(int k = m;k > 1;k >>= 1) {
		for(int i = 0;i < m;i += k) {
			for(int j = i+(k>>1);j < i+k;j ++) {
				int s0 = s[j-(k>>1)],s1 = s[j];
				s[j] = qm((s0 +0ll+ zxy - s1) , zxy);
				s[j-(k>>1)] = qm((s0 +0ll+ s1) , zxy);
			}
		}
	}
	return ;
}
inline void IDWTXOR(int *s,int m) {//异或(逆变换)
	for(int k = 2;k <= m;k <<= 1) {
		for(int i = 0;i < m;i += k) {
			for(int j = i+(k>>1);j < i+k;j ++) {
				int s0 = s[j-(k>>1)],s1 = s[j];
				s[j-(k>>1)] = qm((s0 +0ll+ s1) , zxy) *1ll* inv2 % zxy;
				s[j] = qm((s0 +0ll+ zxy - s1) , zxy) *1ll* inv2 % zxy;
			}
		}
	}
	return ;
}

FWT改进版

KMP板子

for(int i=2,it=0;i<=n;i++){//a为模式串,b为匹配串
	while(it>0&&a[it+1]!=a[i])it=next[it];
	if(a[it+1]==a[i])next[i]=++it;
}
for(int i=1,it=0;i<=m;i++){
	while(it>0&&a[it+1]!=b[i])it=next[it];
	if(a[it+1]==b[i])ans=max(ans,++it);//配得长度为it+1
}

EXKMP板子

for(int i=1,l=0,r=0;i<n;i++){//参考预处理吧,懒得打了
	if(i<=r)e[i]=min(e[i-l],r-i+1);
	while(i+e[i]<n&&s[i+e[i]]==s[e[i]])e[i]++;
	if(i+e[i]-1>r)r=i+e[i]-1,l=i;
}

Manacher板子

//s[0]='$',s[1]='#';//s[0]区别处理
//while((s[n+1]=getchar())!='\n'&&s[n+1]>0)s[n+2]='#',n+=2;
for(int i=1,r=0,mid=0;i<=n;i++){
	if(i<=r)mnc[i]=min(r-i+1,mnc[(mid<<1)-i]);
	while(s[i+mnc[i]]==s[i-mnc[i]])mnc[i]++;
	if(i+mnc[i]-1>r)r=i+mnc[i]-1,mid=i;
	ans=max(ans,mnc[i]-1);
}

AC自动机板子

struct actrie{  //结构体
	int t[26],fail,a;
	inline void CL(){  //清空函数
        memset(t,0,sizeof(t)),fail=a=0;
	}
}t[MAXN];
int IN;
bool v[MAXN];  //vis数组
queue<int>q;
inline void build(){  //读入一个字符串 并 插入至AC自动机
	int p=0;char s=getchar();
	while((s<'a'||s>'z')&&s>0)s=getchar();
	while(s>='a'&&s<='z'){
		if(!t[p].t[s-'a'])t[p].t[s-'a']=++IN,t[IN].CL();
		p=t[p].t[s-'a'],s=getchar();
	}
	if(p>0)t[p].a++;
}
inline void getfail(){  //建立fail(必打)
	while(!q.empty())q.pop();
	for(int i=0;i<26;i++)if(t[0].t[i])t[t[0].t[i]].fail=0,q.push(t[0].t[i]);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;i++){
			if(t[u].t[i])t[t[u].t[i]].fail=t[t[u].fail].t[i],q.push(t[u].t[i]);
			else t[u].t[i]=t[t[u].fail].t[i];
		}
	}
}
inline int acfind(bool cl){  //读入一个字符串 并 查询含多少模式串
	int p=0,ans=0;char s=getchar();
	while((s<'a'||s>'z')&&s>0)s=getchar();
	while(s>='a'&&s<='z'){
		p=t[p].t[s-'a'];
		for(int o=p;o>0&&!v[o];o=t[o].fail)ans+=t[o].a,v[o]=1;
		s=getchar();
	}
    memset(v,0,sizeof(v));
	if(cl)IN=0,t[0].CL();  //cl—是否清空AC自动机
	return ans;
}

后缀数组sa+height

inline void getsa(){
	int q=100; //x[i]为后缀i的第一关键字,y[i]为第二关键字排行为i的后缀
	for(int i=1;i<=q;i++)c[i]=0; //c数组为桶
	for(int i=1;i<=n;i++)x[i]=s[i]-32,c[x[i]]++;
	for(int i=1;i<=q;i++)c[i]+=c[i-1];
	for(int i=n;i>0;i--)sa[c[x[i]]--]=i;
	for(int N=1;N<=n;N<<=1){
		int nm=0;
		for(int i=n;i>n-N;i--)y[++nm]=i;
		for(int i=1;i<=n;i++)if(sa[i]>N)y[++nm]=sa[i]-N;
		for(int i=1;i<=q;i++)c[i]=0;
		for(int i=1;i<=n;i++)c[x[i]]++;
		for(int i=1;i<=q;i++)c[i]+=c[i-1];
		for(int i=n;i>0;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
		swap(x,y); //把y复制为上一个x数组,x变为空,求新的x数组↓
		x[sa[1]]=1,nm=1;
		for(int i=2;i<=n;i++)
			x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+N]==y[sa[i-1]+N])?nm:++nm;
		if(nm==n)break;
		q=nm;
	}
	for(int i=1;i<=n;i++)rk[sa[i]]=i;
}
inline void getheight(){
	for(int i=1,k=0;i<=n;i++){
		ht[rk[i]]=0;
		if(rk[i]==1)continue;
		k=max(k-1,0);
		int j=sa[rk[i]-1];
		while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;//暴力扩宽
		ht[rk[i]]=k;
	}
}

后缀自动机

//非广义
struct itn{
	int ch[26],len,fa;
	itn(){memset(ch,0,sizeof(ch)),len=0;}
}sam[MAXN];
int las=1,tot=1;
inline void samadd(int c){
	int p=las,np=las=++tot;sam[np].len=sam[p].len+1;
	for(;p&&!sam[p].ch[c];p=sam[p].fa)sam[p].ch[c]=np;
	if(!p)sam[np].fa=1;
	else{int q=sam[p].ch[c],nq;
		if(sam[q].len==sam[p].len+1)sam[np].fa=q;
		else{
			nq=++tot,sam[nq]=sam[q];
//很多博客说该SAM复杂度是O(n)的,其实不是,它的复杂度为O(n*字符种数),原因就在上面一句和初始化时的memset
//只有把每个点的出边用vector存下来才是O(n),但是不好打
            sam[nq].len=sam[p].len+1,sam[q].fa=sam[np].fa=nq;
			for(;p&&sam[p].ch[c]==q;p=sam[p].fa)sam[p].ch[c]=nq;
		}
	}
}

回文自动机

struct itn{
	int len,siz,num,ch[30],fail;
}td[MAXN];
int IN=1,las=0;
char s[MAXN];
inline void build(){//初始化,一般打在主函数里
	td[1].len=-1,td[0].fail=td[1].fail=1;
}
inline int getf(int x,int n){
	while(s[n-td[x].len-1]!=s[n])x=td[x].fail;
	return x;
}
inline int extend(int n){//插入操作,返回以第n个字符为结尾的回文串的个数
	int cur=getf(las,n),np=td[cur].ch[s[n]-'a'];
	if(!np){
		np=++IN,td[np].len=td[cur].len+2;
		td[np].fail=td[getf(td[cur].fail,n)].ch[s[n]-'a'];
		td[cur].ch[s[n]-'a']=np,td[np].num=td[td[np].fail].num+1;
	}
	td[np].siz++,las=np;
	return td[np].num;
}

猜你喜欢

转载自blog.csdn.net/weixin_43960287/article/details/108245309