Resuelva el problema de Luogu P6378 [[PA2010] Riddle]

En primer lugar, no es difícil ver la relación límite entre los puntos de este problema, podemos considerar usar \ (2-SAT \) para resolver, conectando un borde dirigido desde el estado \ (x \) al estado \ (y \) Significa que si el estado \ (x \) existe, entonces el estado \ (y \) debe existir.

En el siguiente procesamiento, \ (x \) indica que el punto \ (x \) está seleccionado como punto clave, \ (x ^ \ prime \) indica que el punto \ (x \) no está seleccionado como punto clave, \ (x \ longrightarrow y \) indica que hay un borde dirigido entre los dos estados.

Para la restricción de que al menos un punto final de cada borde es un punto clave, si ambos extremos del borde son \ (x \) e \ (y \) , entonces proceda directamente a \ (x ^ \ prime \ longrightarrow y \, \ y ^ \ prime \ longrightarrow x \) .

Cuando se trata de la limitación de que cada parte tiene exactamente un punto clave, se encuentra que una operación como conectar un borde a todos los puntos de la parte, excepto que es necesario, para que el número de bordes alcance \ (n ^ 2 \) Este nivel no es aceptable, por lo que deberíamos considerar optimizar la construcción.

Para cada punto, lo consideramos en la parte donde está ubicado, cada punto está representado por \ (a_i \) , y luego se agrega un nuevo estado \ (pre_i \) , lo que significa que está delante de la parte \ (i \ ) Si se selecciona un punto como punto clave.

Podemos realizar las siguientes operaciones al conectar bordes:

\ (a_i \ longrightarrow pre_ {a_i} \, \ pre_ {a_i} ^ \ prime \ longrightarrow a_i ^ \ prime \)

\ (pre_ {a_ {i-1}} \ longrightarrow pre_ {a_i} \, \ pre_ {a_ {i-1}} \ longrightarrow a_i ^ \ prime \)

\ (pre_ {a_i} ^ \ prime \ longrightarrow pre_ {a_ {i-1}} ^ \ prime \, \ a_i \ longrightarrow pre_ {a_ {i-1}} ^ \ prime \)

Con dicho prefijo para optimizar la construcción, no hay necesidad de conectar tantos lados, y las limitaciones del problema también se pueden cumplir.

Vea el código para detalles específicos de implementación.

\(código:\)

#include<bits/stdc++.h>
#define p1(x) x
#define p0(x) x+n
#define pre1(x) x+2*n
#define pre0(x) x+3*n
#define maxn 8000010
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,m,k,dfn_cnt,co_cnt,top;
int dfn[maxn],low[maxn],co[maxn],st[maxn],a[maxn];
bool vis[maxn];
struct edge
{
    int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
    e[++edge_cnt]=(edge){to,head[from]};
    head[from]=edge_cnt;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++dfn_cnt;
    st[++top]=x,vis[x]=true;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
        else if(vis[y]) low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x])
    {
        co_cnt++;
        int now;
        do
        {
            now=st[top--];
            vis[now]=false;
            co[now]=co_cnt;
        }while(now!=x);
    }
}
bool check()
{
    for(int i=1;i<=4*n;++i)
        if(!dfn[i])
            tarjan(i);
    for(int i=1;i<=n;++i)
        if(co[p1(i)]==co[p0(i)]||co[pre1(i)]==co[pre0(i)])
            return false;
    return true;
}
int main()
{
    read(n),read(m),read(k);
    for(int i=1;i<=m;++i)
    {
        int x,y;
        read(x),read(y);
        add(p0(x),p1(y)),add(p0(y),p1(x));
    }
    for(int i=1;i<=k;++i)
    {
        int w;
        read(w);
        for(int j=1;j<=w;++j)
            read(a[j]),add(p1(a[j]),pre1(a[j])),add(pre0(a[j]),p0(a[j]));
        for(int j=2;j<=w;++j)
        {
            add(pre1(a[j-1]),pre1(a[j])),add(pre1(a[j-1]),p0(a[j]));
            add(pre0(a[j]),pre0(a[j-1])),add(p1(a[j]),pre0(a[j-1]));
        }
    }
    if(check()) puts("TAK");
    else puts("NIE");
    return 0;
}

Supongo que te gusta

Origin www.cnblogs.com/lhm-/p/12695332.html
Recomendado
Clasificación