Códigos prácticos comunes para competiciones de C ++ (4)

Prefacio

Debido a que los siguientes tableros están relacionados con cadenas, no me molesto en escribir "Códigos prácticos comunes para competiciones de C ++ (5)", solo ponga una pieza, por lo que la cantidad de código en este artículo puede ser la mayor.

Explique brevemente por qué la mayoría de las estructuras en mi código se llaman "itn". Esto se debe a que cuando aprendí a escribir código en los primeros días, a menudo escribía mal "int" como "itn", así que ayudé a distinguirlas al escribir estructuras.

Todos los códigos no agregados por defecto agregan "#define ll long long"

Página anterior: Códigos comunes y prácticos para competiciones de C ++ (3)

Tabla de contenido

Anillo de clasificación topológico

División de cadena de árbol

Plantilla de tratamiento no giratoria

Estructura de número racional (puntuación anti-jamming)

Busca el centro de gravedad del árbol.

Prim busca el árbol de expansión mínimo (utilizado principalmente para gráficos completos)

Tablero de Li Chaoshu (punto de apertura dinámico)

Fusión de árbol de segmentos

Transformada rápida de Walsh

Tablero KMP

Tablero EXKMP

Tablero de Manacher

Tablero de autómatas de CA

Sufijo matriz sa + altura

Autómatas de sufijo

Autómatas palíndromos


Siguiente: Códigos prácticos comunes para competiciones de C ++ (5)

Anillo de clasificación topológico

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;
}

División de cadena de árbol

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;
}
//其它的都是线段树的板子,不想水了

Plantilla de tratamiento no giratoria

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);
}
//有了分裂和合并,其它操作有手就能打,这里就不用水了

Estructura de número racional (puntuación anti-jamming)

#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;
	}
};

Busca el centro de gravedad del árbol.

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 busca el árbol de expansión mínimo (utilizado principalmente para gráficos completos)

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;
}

Tablero de Li Chaoshu (punto de apertura dinámico)

#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;
}

Fusión de árbol de segmentos

Tome el árbol Li Chao de apertura dinámica directamente como ejemplo (las funciones relacionadas son las anteriores)

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;
}

Transformada rápida de Walsh FWT

Transferencia de la explicación: 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 ;
}

Versión mejorada de FWT

Tablero 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
}

Tablero 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;
}

Tablero de 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);
}

Tablero de autómatas de CA

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;
}

Sufijo matriz sa + altura

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;
	}
}

Autómatas de sufijo

//非广义
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;
		}
	}
}

Autómatas palíndromos

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;
}

 

Supongo que te gusta

Origin blog.csdn.net/weixin_43960287/article/details/108245309
Recomendado
Clasificación