现实

题目大意

求删去一个点后图变成DAG这样的点的所有选择方案。

分析

P20

暴力枚举每个点,
然后拓扑排序查看是否为DAG。

P40

这一档满足有一条链从1连到n,有一条边为(n,1)。
其实就是一个大环。
这样其他的边形成的环是很明显的。
所以直接枚举点,然后查看是否被所有这样的边包含即可。
随便找个什么数据结构都能维护。
差分可以写得比较方便。

P100

可知现在找的点必须要使当前图上所有环消失。
因此它肯定在一个环上,
这时进行一个简单的处理,裂一个点为入点与出点,
那么删除该边与删除该点的效果是相同的。
然后就随便找一个环(找不到说明原来就是个DAG。
再删掉这个环上所有的边,
对剩下的点进行拓扑排序。
如果还不是DAG则肯定无解。
然后根据这个拓扑序来对每个点求解其能通过非环边到达的位置。
将这个二元组看作一条环上的有方向的边。
于是变成了P40的情况,同样差分维护。

代码

初始化失误调了很久= =

#include
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define INF 0x3f3f3f3f
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define M 1000004

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}

int n,m,N;
int Next[M<<1],V[M<<1],Head[M],tot;
void Add_Edge(int u,int v){
    Next[++tot]=Head[u],V[Head[u]=tot]=v;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])

int Pre[M],Fa[M],Ans[M],ans;
int C[M],Clen;
bool FindC,Use[M<<1];
int Vis[M];
void Circle(int A){
    Vis[A]=1;
    LREP(i,A){
        int B=V[i];
        if(Vis[B]==0){
            Fa[B]=A;
            Pre[B]=i;
            Circle(B);
        }
        else if(Vis[B]==1){
            int x=A;
            Use[i]=1;
            while(x!=B){
                C[Clen++]=x;
                Use[Pre[x]]=1;
                x=Fa[x];
            }
            C[Clen++]=B;
            FindC=1;
        }
        if(FindC)return;
    }
    Vis[A]=2;
}
int Deg[M],Tp[M],LT,Tlen;
bool Topo(){
    REP(i,1,N+1) LREP(j,i) if(!Use[j]) Deg[V[j]]++;
    REP(i,1,N+1) if(!Deg[i]) Tp[Tlen++]=i;
    while(LTif(!Use[i] && !(--Deg[V[i]]))
        Tp[Tlen++]=V[i];
    return Tlen==N;
}
int Mn[M],Mx[M],Fr[M],Sum[M];
void Solve(){
    REP(i,1,n+1)if(!Vis[i]){
        Circle(i);
        if(FindC)break;
    }
    if(!FindC){
        REP(i,1,n+1)
            Ans[ans++]=i;
        return;
    }
    if(!Topo())return;

    memset(Mn,63,sizeof(Mn));
    memset(Mx,-1,sizeof(Mx));
    memset(Fr,-1,sizeof(Fr));
    reverse(C,C+Clen);
    REP(i,0,Clen)Mn[C[i]]=Mx[C[i]]=Fr[C[i]]=i;
    int A;
    DREP(i,N-1,-1) LREP(j,A=Tp[i]) if(!Use[j]) chkmin(Mn[A],Mn[V[j]]),chkmax(Mx[A],Mx[V[j]]);
    REP(i,0,N) LREP(j,A=Tp[i]) if(!Use[j]) chkmax(Fr[V[j]],Fr[A]);
    REP(i,0,Clen){
        A=C[i];
        if(Mn[A]0]++,Sum[Mn[A]+1]--;
            Sum[i+1]++;
        }
        if(Mx[A]>i){
            Sum[i+1]++;
            Sum[Mx[A]+1]--;
        }
        if(Fr[A]>i){
            Sum[0]++,Sum[i]--;
            Sum[Fr[A]+1]++;
        }
    }
    REP(i,0,Clen){
        Sum[i+1]+=Sum[i];
        if(!Sum[i] && C[i]>n)Ans[ans++]=C[i]-n;
    }
}
int main(){
    Rd(n),Rd(m);N=n<<1;
    REP(i,1,n+1)Add_Edge(i,i+n);
    REP(i,0,m){
        int u,v;
        Rd(u),Rd(v);
        Add_Edge(u+n,v);
    }
    Solve();
    printf("%d\n",ans);
    sort(Ans,Ans+ans);
    REP(i,0,ans)
        printf("%d%c",Ans[i]," \n"[i==ans-1]);
    return 0;
}

你可能感兴趣的:(图论差不多是背背默默)