UOJ Logo zhouzixuan的博客

博客

bzoj 4424 Codeforces19E Fairy

2016-03-17 14:16:20 By zhouzixuan
【题目描述】:
    给定 n 个点,m 条边的无向图,可以从图中删除一条边,问删除哪些边可以使图变成一个二分图。
【题目解法】:
    考虑dfs出一颗生成树
    然后我们记录一下哪些返租边构成偶环,哪些构成奇环
    如果图中没有奇环,显然每条边都可以删去(自环的情况后面再说)
    考虑哪些返祖边可以被删去:
    1.图中存在奇环,但仅存在一个,那么这个奇环的返祖边可以被删去
    考虑哪些树边可以被删去(下列条件必须两个都满足):
    1.是所有奇环的并集
    2.不属于任何偶环
    证明:1显然,考虑2,如果属于某个偶环,因为他也属于某个奇环,那么删了它两个环并起来还是一个奇环,因此不合法
    所以我们处理出每一条树边经过它的偶环和奇环个数
    然后先处理一些细节:
    1.重边,我们把重边当成偶数环看待就好了
    2.自环,显然自环必须被删去,因此:
      1.如果图中没有奇环,那么只能删去自环
      2.如果图中存在奇环,答案为0
    奇环和偶环处理直接维护树上前缀和+lca既可以了
    然后就一直WA,发现没有把答案sort一遍(居然在cf上面水过了前30个点TAT)
    然后改了PE,发现不能输出行末空格
    然后改了接着WA,发现如果答案为0,第二行不能多输出一个换行TAT
    然后就A了
    复杂度O(N+M)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=1000005;
const int M=2000005;
struct Edge {int x,y,id; bool flag;} B[M];
struct edge {int x,id,next;} b[M];
int n,m,tot,a[N],self,odd,ans,Ans[N],selfid;
int f[N],g[N],dep[N],fa[21][N],cntf,cntg,fid,faid[N];
inline long long getint()
{
    long long x=0; char c=getchar(); bool flag=false;
    while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
    if (c=='-') flag=true,c=getchar();
    while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
    if (flag) return -x; else return x;
}
inline void addedge(int x,int y,int id)
{
    ++tot;
    b[tot].x=y;
    b[tot].id=id;
    b[tot].next=a[x];
    a[x]=tot;
}
void init()
{
    n=getint(); m=getint();
    for (int i=1; i<=m; i++)
    {
        B[i].x=getint(); B[i].y=getint(); B[i].id=i;
        if (B[i].x==B[i].y) self++,selfid=i;
    }
}
namespace MST
{
    int fa[N];
    int getfather(int x)
    {
        if (fa[x]==x) return x;
        return fa[x]=getfather(fa[x]);
    }
    void solve()
    {
        for (int i=1; i<=n; i++) fa[i]=i;
        for (int i=1; i<=m; i++)
        {
            int x=B[i].x,y=B[i].y; if (x==y) continue;
            int fx=getfather(x),fy=getfather(y); if (fx==fy) continue;
            fa[fy]=fx; B[i].flag=true; addedge(x,y,i); addedge(y,x,i);
        }
    }
}
namespace Build
{
    bool v[N];
    void dfs(int x)
    {
        v[x]=true;
        for (int p=a[x];p;p=b[p].next)
        {
            int pp=b[p].x; if (pp==fa[0][x]) continue;
            fa[0][pp]=x; dep[pp]=dep[x]+1; faid[pp]=b[p].id; dfs(pp);
        }
    }
    inline int LCA(int x,int y)
    {
        if (dep[x]<dep[y]) swap(x,y);
        for (int k=dep[x]-dep[y],j=0;k;k/=2,j++) if (k&1) x=fa[j][x];
        if (x==y) return x;
        for (int i=20; i>=0; i--) if (fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y];
        return fa[0][x];
    }
    inline int dist(int x,int y)
    {
        int k=LCA(x,y);
        return dep[x]+dep[y]-2*dep[k]+1;
    }
    void DFS(int x)
    {
        v[x]=true;
        for (int p=a[x];p;p=b[p].next)
        {
            int pp=b[p].x; if (pp==fa[0][x]) continue;
            DFS(pp); f[x]+=f[pp]; g[x]+=g[pp];
        }
    }
    void solve()
    {
        for (int i=1; i<=n; i++) if (!v[i]) {dep[i]=1; dfs(i);}
        for (int i=1; i<=20; i++) for (int j=1; j<=n; j++) fa[i][j]=fa[i-1][fa[i-1][j]];
        for (int i=1; i<=m; i++)
        {
            int x=B[i].x,y=B[i].y; if (x==y) continue;
            if (B[i].flag) continue; int k=LCA(x,y),dis=dist(x,y);
            if (dis%2==0) cntg++,g[x]++,g[y]++,g[k]-=2;
            if (dis%2==1) cntf++,f[x]++,f[y]++,f[k]-=2,fid=i;
        }
        memset(v,false,sizeof(v));
        for (int i=1; i<=n; i++) if (!v[i]) DFS(i);
    }
}
inline bool special()
{
    if (self!=0)
    {
        if (self>1) {printf("0\n"); return true;}
        if (cntf==0) {printf("1\n"); printf("%d\n",selfid); return true;}
        printf("0\n"); return true;
    }
    if (cntf==0)
    {
        printf("%d\n",m);
        for (int i=1; i<=m-1; i++) printf("%d ",i); printf("%d\n",m);
        return true;
    }
    return false;
}
namespace NoneTree
{
    void solve()
    {
        if (cntf>1) return;
        Ans[++ans]=fid;
    }
}
namespace Tree
{
    bool v[N];
    void dfs(int x)
    {
        if ((faid[x]!=0)&&(f[x]==cntf)&&(g[x]==0)) Ans[++ans]=faid[x]; v[x]=true;
        for (int p=a[x];p;p=b[p].next) if (b[p].x!=fa[0][x]) dfs(b[p].x);
    }
    void solve()
    {
        for (int i=1; i<=n; i++) if (!v[i]) dfs(i);
        printf("%d\n",ans); if (ans==0) return; sort(Ans+1,Ans+ans+1);
        for (int i=1; i<=ans-1; i++) printf("%d ",Ans[i]); printf("%d\n",Ans[ans]);
    }
}
int main()
{
    init();
    MST::solve();
    Build::solve();
    if (special()) return 0;
    NoneTree::solve();
    Tree::solve();
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。