[Algoritmo] Orden de árbol DFS y orden de Euler-dfs marca de tiempo y orden de acceso de bucle



Escriba en los métodos de entrada frontal y almacenamiento de imágenes utilizados en este artículo

Método de entrada:

  Los dos números ny raíz en la primera línea indican que el árbol tiene un total de n nodos, de los cuales el número raíz es el nodo raíz

  Siguiente n-1 líneas, dos enteros ab por línea, que indican que el nodo a está conectado al nodo b

const int MAXN=1e4+50;

int dfs_order[MAXN];
int euler_order1[MAXN];
int euler_order2[MAXN];

bool vis[MAXN]; //访问标记
vector<int> G[MAXN]; //邻接表存图
int pos;
int n,root,a,b;
scanf("%d%d",&n,&root);
for(int i=1;i<n;i++)
{
    scanf("%d%d",&a,&b);
    G[a].push_back(b);
    G[b].push_back(a);//双向存边
}



Orden DFS

Como su nombre lo indica, el orden DFS representa el orden de acceso de los dfs desde el nodo raíz del árbol.

También se puede usar como marca de tiempo para visitar cada nodo

PIC1

Tome este árbol como ejemplo, el nodo raíz es 1

Buscar en orden de izquierda a derecha, luego su orden de búsqueda es

PIC2

El orden en la figura se expresa como

PIC3



Código de búsqueda de orden DFS

void dfs(int p)
{
    dfs_order[++pos]=p; //访问到节点p时,++pos作为访问到的时间
    vis[p]=true;//标记访问
    for(int i:G[p])//再搜索未访问过的与p相邻的节点
        if(!vis[i])
            dfs(i);
}



Orden de Euler

El orden de Euler se ve casi igual que el orden de dfs

La ruta almacenada comienza desde el nodo raíz, pasa por todos los puntos en el orden de dfs y luego regresa al origen

Hay dos tipos de orden de Euler



Orden de Euler 1

Este tipo de orden de Euler es equivalente a registrar el número de nodo en la parte superior de la pila si la pila del nodo de almacenamiento cambia una vez durante dfs

En otras palabras, cada vez que visita el subárbol de un nodo, debe volver al nodo una vez y luego continuar buscando los subárboles restantes del nodo

El proceso de movimiento en el árbol es

PIC4

Su orden de búsqueda es

PIC5



Código de búsqueda para la orden de Euler 1

void euler_dfs1(int p)
{
    euler_order1[++pos]=p;
    vis[p]=true;
    for(int i:G[p])
        if(!vis[i])
        {
            euler_dfs1(i);
            euler_order1[++pos]=p; //与dfs序的差别,在搜索完一棵子树后就折返一次自己
        }
} //数组需要开2倍n大



Euler orden 2

Este tipo de orden de Euler es equivalente a cuando un nodo se coloca en la pila en dfs, el nodo se registra hasta que el nodo se elimina de la pila en operaciones posteriores, y el nodo se vuelve a grabar.

En otras palabras, cada nodo aparecerá estrictamente en el registro dos veces, la primera vez es cuando se busca y la segunda es cuando su subárbol se busca por completo

Excepto por el nodo raíz, cada nodo es estrictamente dos grados y dos grados

El proceso de movimiento en el árbol es

PIC6

Su orden de búsqueda es

PIC7

Se puede encontrar que el intervalo encerrado por dos ocurrencias de un determinado nodo en la secuencia indica que este nodo y su subárbol



Código de búsqueda para la orden 2 de Euler

void euler_dfs2(int p)
{
    euler_order2[++pos]=p;
    vis[p]=true;
    for(int i:G[p])
        if(!vis[i])
            euler_dfs2(i);
    euler_order2[++pos]=p; //与dfs序的差别,在所有子树搜索完后再折返自己
} //数组需要开2倍n大



Entrada de muestra y programa


Entrada de muestra:

9 1
1 2
1 3
2 4
2 5
3 6
4 7
4 8
4 9

Salida de muestra:

DFS Order :
1 2 4 7 8 9 5 3 6
Euler Order 1 :
1 2 4 7 4 8 4 9 4 2 5 2 1 3 6 3 1
Euler Order 2 :
1 2 4 7 7 8 8 9 9 4 5 5 2 3 6 6 3 1


Programa de plantilla:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e4+50;

int dfs_order[MAXN],euler_order1[MAXN*2],euler_order2[MAXN*2];

bool vis[MAXN];
vector<int> G[MAXN];
int pos;

void dfs(int p)
{
    dfs_order[++pos]=p;
    vis[p]=true;
    for(int i:G[p])
        if(!vis[i])
            dfs(i);
}

void euler_dfs1(int p)
{
    euler_order1[++pos]=p;
    vis[p]=true;
    for(int i:G[p])
        if(!vis[i])
        {
            euler_dfs1(i);
            euler_order1[++pos]=p;
        }
}

void euler_dfs2(int p)
{
    euler_order2[++pos]=p;
    vis[p]=true;
    for(int i:G[p])
        if(!vis[i])
            euler_dfs2(i);
    euler_order2[++pos]=p;
}

int main()
{
    int n,root,a,b;
    scanf("%d%d",&n,&root);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        G[a].push_back(b);
        G[b].push_back(a);
    }

    puts("DFS Order :");
    memset(vis,false,n+5);
    pos=0;
    dfs(root);
    for(int i=1;i<=pos;i++)
        printf("%d ",dfs_order[i]);
    putchar('\n');

    puts("Euler Order 1 :");
    memset(vis,false,n+5);
    pos=0;
    euler_dfs1(root);
    for(int i=1;i<=pos;i++)
        printf("%d ",euler_order1[i]);
    putchar('\n');

    puts("Euler Order 2 :");
    memset(vis,false,n+5);
    pos=0;
    euler_dfs2(root);
    for(int i=1;i<=pos;i++)
        printf("%d ",euler_order2[i]);
    putchar('\n');

    return 0;
}



Ejemplos de aplicación


1 - Codeforces 1006E

1006E. Problema militar

Prob-1-1
Prob-1-2
Prob-1-3

preguntas desnudas en orden dfs

Un árbol de n nodos, cada borde es un borde unidireccional, y el número de n-1 en la segunda fila corresponde al número de nodo principal 2,3,4 ... nodo

q consultas, cada consulta contiene dos números uk, se requiere que la salida cuente desde k hasta el k-ésimo nodo en el orden dfs , si el nodo no existe en el subárbol de u, salida -1

Registre el tiempo de entrada, apilado de un nodo cuando se procesa la secuencia dfs, y el nodo realmente representado por la secuencia dfs.

Si dentro [u] + k-1> fuera [u], este nodo no está incluido en el subárbol de u

De lo contrario, salida dfs_order [en [u] + k-1]

#include<bits/stdc++.h>
using namespace std;

vector<int> G[200050];
int dfs_order[200050],in[200050],out[200050],pos;

void dfs(int p)
{
    in[p]=++pos;
    dfs_order[pos]=p;
    for(int it:G[p])
        dfs(it);
    out[p]=pos;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int n,q,d,a,b;
    cin>>n>>q;
    for(int i=1;i<n;i++)
    {
        cin>>d;
        G[d].push_back(i);
    }
    pos=0;
    dfs(1);
    while(q--)
    {
        cin>>a>>b;
        if(in[a]+b-1>out[a])
            cout<<"-1\n";
        else
            cout<<dfs_order[in[a]+b-1]<<'\n';
    }
    return 0;
}



2 - LibreOJ #144

# 144. Secuencia DFS 1

Prob-2

El derecho a ser el valor de los árboles modificaciones puntuales y sub-árbol y consultas

Puede mantener una matriz tipo árbol con las marcas de tiempo en orden dfs como índice

Registre el tiempo de entrada y salida de cada nodo en la pila

Entonces este nodo y su subárbol están en el intervalo [in, out]

Para operaciones de consulta, simplemente tome sum (out) -sum (in-1) como respuesta

La operación de modificación solo necesita modificar el valor del punto en el tiempo de apilamiento en

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n,ar[1000050],in[1000050],out[1000050],pos=0;
bool vis[1000050];
vector<int> G[1000050];
ll tree[1000050];

int lowbit(int x)
{
    return x&(-x);
}
void add(int p,ll d)
{
    while(p<=n)
    {
        tree[p]+=d;
        p+=lowbit(p);
    }
}
ll sum(int p)
{
    ll r=0;
    while(p>0)
    {
        r+=tree[p];
        p-=lowbit(p);
    }
    return r;
}

void dfs(int p)
{
    in[p]=++pos; //入栈时间
    vis[p]=true;
    for(int it:G[p])
        if(!vis[it])
            dfs(it);
    out[p]=pos; //出栈时间
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int m,r,a,b,kd;
    cin>>n>>m>>r;
    for(int i=1;i<=n;i++)
        cin>>ar[i];
    for(int i=1;i<n;i++)
    {
        cin>>a>>b;
        G[a].push_back(b);
        G[b].push_back(a);
    }
    dfs(r);
    for(int i=1;i<=n;i++)
        add(in[i],ar[i]); //以时间作为索引建立树状数组
    while(m--)
    {
        cin>>kd;
        if(kd==1)
        {
            cin>>a>>b;
            add(in[a],b);
        }
        else
        {
            cin>>a;
            cout<<sum(out[a])-sum(in[a]-1)<<'\n';
        }
    }
    return 0;
}



3 - LibreOJ #145

# 145. Secuencia DFS 2

Prob-3

Del peso del árbol eran toda modificaciones sub-árboles y sub-árbol y consultas

Similar a la pregunta anterior

Puede mantener un árbol de segmentos de línea utilizando las marcas de tiempo en orden dfs como índice

Aún de acuerdo con el tiempo de pila y el tiempo de espera como índice

Luego configure un árbol de líneas con modificación y suma de intervalos.

Y debido a que un nodo del árbol de segmento de línea mantiene un intervalo, es decir, un período de tiempo

Por lo tanto, preste atención al construir un árbol y luego introduzca una matriz anti_in para registrar los datos opuestos a la matriz in

El uso específico es el siguiente

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e6+50;

int n,ar[MAXN];
bool vis[MAXN];
vector<int> G[MAXN];
int in[MAXN],out[MAXN],pos=0,anti_in[MAXN];

struct node
{
	ll l,r,sum,lazy;
}tree[MAXN*4];

void push_up(int id)
{
	tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
}

void push_down(int id)
{
	if(tree[id].lazy)
	{
		int m=tree[id].r-tree[id].l+1;
		tree[id<<1].lazy+=tree[id].lazy;
		tree[id<<1|1].lazy+=tree[id].lazy;
		tree[id<<1].sum+=tree[id].lazy*(m-(m>>1));
		tree[id<<1|1].sum+=tree[id].lazy*(m>>1);
		tree[id].lazy=0;
	}
}

void buildTree(int id,int l,int r)
{
	tree[id].l=l;
	tree[id].r=r;
	tree[id].lazy=0;
	if(l==r)
	{
		tree[id].sum=ar[anti_in[l]]; //这里引用的是第l(或r)的时间访问到的节点id传给ar数组获取原有的权值
		return;
	}
	ll mid=(l+r)>>1;
	buildTree(id<<1,l,mid);
	buildTree(id<<1|1,mid+1,r);
	push_up(id);
}

void update(int id,int L,int R,ll val)
{
	if(L<=tree[id].l&&R>=tree[id].r)
	{
		tree[id].sum+=val*(tree[id].r-tree[id].l+1);
		tree[id].lazy+=val;
		return;
	}
	push_down(id);
	int mid=(tree[id].r+tree[id].l)>>1;
	if(L<=mid)
		update(id<<1,L,R,val);
	if(R>mid)
		update(id<<1|1,L,R,val);
	push_up(id);
}

ll query_sum(int id,int L,int R)
{
	if(L<=tree[id].l&&R>=tree[id].r)
		return tree[id].sum;
	push_down(id);
	int mid=(tree[id].r+tree[id].l)>>1;
	ll ans=0;
	if(L<=mid)
		ans+=query_sum(id<<1,L,R);
	if(R>mid)
		ans+=query_sum(id<<1|1,L,R);
	return ans;
}

void dfs(int p)
{
	in[p]=++pos; //in记录节点p入栈的时间
	anti_in[pos]=p; //anti_in记录在某个时间访问到的节点的id
	vis[p]=true;
	for(int it:G[p])
		if(!vis[it])
			dfs(it);
	out[p]=pos;
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int m,r,a,b,kd;
	cin>>n>>m>>r;
	for(int i=1;i<=n;i++)
		cin>>ar[i];
	for(int i=1;i<n;i++)
	{
		cin>>a>>b;
		G[a].push_back(b);
		G[b].push_back(a);
	}
	dfs(r);
	buildTree(1,1,n); //建立线段树
	while(m--)
	{
		cin>>kd;
		if(kd==1)
		{
			cin>>a>>b;
			update(1,in[a],out[a],b);
		}
		else
		{
			cin>>a;
			cout<<query_sum(1,in[a],out[a])<<'\n';
		}
	}
	return 0;
}

Supongo que te gusta

Origin www.cnblogs.com/stelayuri/p/12702684.html
Recomendado
Clasificación