bzoj千题计划229:bzoj4424: Cf19E Fairy

http://www.lydsy.com/JudgeOnline/problem.php?id=4424

图是二分图的条件:没有奇环

所以,如果图不存在奇环,删除任意一条边都可以

如果存在奇环,

对于树边来说:

那么可能可以删除的边一定在所有奇环的交集内

而且这条边不能在偶环内

因为如果一条边既是奇环上的一条边,又是偶环上的一条边

删除这条边后,这个奇环和偶环会合并成一个新的奇环

所以最终的答案= 奇环的交集-偶环的并集

对于非树边来说:

如果只有一个奇环,那么可以删除构成环的这条非树边

树边和非树边的判断:并查集

奇环交与偶环并:差分,统计树上后缀和

#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

#define N 1000001

int e[N][2],ty[N];

int tot=1;
int front[N],nxt[N<<1],to[N<<1],id[N<<1];

int F[N];

int maxd;
int fa[N][21];

int dep[N];

int odd_cir,sum[N];

int ans_tot,ans[N];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-‘0‘; c=getchar(); }
}

int find(int i) { return F[i]==i ? i : F[i]=find(F[i]); }

void add(int u,int v,int num)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; id[tot]=num;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; id[tot]=num;
}

void dfs(int u)
{
    for(int i=1;i<=maxd;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
    int t;
    for(int i=front[u];i;i=nxt[i])
    {
        t=to[i];
        if(t==fa[u][0]) continue;
        dep[t]=dep[u]+1;
        fa[t][0]=u;
        dfs(t);
    }
}

int cal(int x)
{
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=fa[x][0])
        {
            cal(to[i]);
            if(sum[to[i]]==odd_cir) ans[++ans_tot]=id[i];
            sum[x]+=sum[to[i]];
        }
}

int get_lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    int cnt=dep[u]-dep[v];
    for(int i=maxd;i>=0;--i)
        if(cnt&(1<<i)) u=fa[u][i];
    if(u==v) return u;
    for(int i=maxd;i>=0;--i)
        if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}

int main()
{
    int n,m;
    int u,v,fu,fv;
    read(n); read(m);
    for(int i=1;i<=n;++i) F[i]=i;
    for(int i=1;i<=m;++i)
    {
        read(u); read(v);
        fu=find(u); fv=find(v);
        if(fu!=fv)
        {
            ty[i]=1;
            F[fu]=fv;
            add(u,v,i);
        }
        e[i][0]=u; e[i][1]=v;
    }
    maxd=log(n)/log(2);
    for(int i=1;i<=n;++i)
        if(find(i)==i) dep[i]=1,dfs(i);
    int self=0;
    int lca;
    for(int i=1;i<=m;++i)
    {
        u=e[i][0]; v=e[i][1];
        if(u==v && self) { printf("0"); return 0; }
        if(u==v) { self=i; continue; }
        if(!ty[i])
        {
            lca=get_lca(u,v);
            if((dep[u]-dep[lca]+dep[v]-dep[lca])&1)
            {
                sum[v]--;
                sum[u]--;
                sum[lca]+=2;
            }
            else
            {
                ty[i]=2;
                odd_cir++;
                sum[v]++;
                sum[u]++;
                sum[lca]-=2;
            }
        }
    }
    if(self)
    {
        if(!odd_cir) printf("1\n%d",self);
        else printf("0");
        return 0;
    }
    if(!odd_cir)
    {
        printf("%d\n",m);
        for(int i=1;i<m;++i) printf("%d ",i);
        if(m) printf("%d",m);
        return 0;
    }
    for(int i=1;i<=n;++i)
        if(find(i)==i) cal(i);
    if(odd_cir==1)
        for(int i=1;i<=m;++i)
            if(ty[i]==2)
            {
                ans[++ans_tot]=i;
                break;
            }
    sort(ans+1,ans+ans_tot+1);
    printf("%d\n",ans_tot);
    for(int i=1;i<ans_tot;++i) printf("%d ",ans[i]);
    if(ans_tot) printf("%d",ans[ans_tot]);
}

4424: Cf19E Fairy

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 740  Solved: 187
[Submit][Status][Discuss]

Description

给定 n 个点,m 条边的无向图,可以从图中删除一条边,问删除哪些边可以使图变成
一个二分图。

Input

第 1 行包含两个整数 n,m。分别表示点数和边数。
第 2 到 m+1 行每行两个数 x,y 表示有一条(x,y)的边。

Output

输出第一行一个整数,表示能删除的边的个数。
接下来一行按照从小到大的顺序输出边的序号。

Sample Input

4 4
1 2
1 3
2 4
3 4

Sample Output

4
1 2 3 4

HINT

100%的数据,n,m<=1000000

原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8428557.html

时间: 2024-08-29 17:41:49

bzoj千题计划229:bzoj4424: Cf19E Fairy的相关文章

bzoj千题计划185:bzoj1260: [CQOI2007]涂色paint

http://www.lydsy.com/JudgeOnline/problem.php?id=1260 区间DP模型 dp[l][r] 表示涂完区间[l,r]所需的最少次数 从小到大们枚举区间[l,r] 如果col[l]==col[r] dp[l][r]=min(dp[l+1][r],dp[l][r-1],dp[l+1][r-1]+1) 否则 dp[l][r]=min(dp[l][k]+dp[k+1][r]) 我还是辣鸡啊~~~~(>_<)~~~~,这种题都不能秒 #include<c

bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹

http://www.lydsy.com/JudgeOnline/problem.php?id=2244 每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数 pre_len [i] 表示以i结尾的最长不下降子序列的长度 pre_sum[i] 表示对应长度下的方案数 suf_len[i] 表示以i开头的最长不下降子序列长度 suf_sum[i] 表示对应长度下的方案数 若已有了这4个数组 设最长上升子序列长度=mx 那么 如果pre_len[i]+suf_len[i] -

bzoj千题计划304:bzoj3676: [Apio2014]回文串

https://www.lydsy.com/JudgeOnline/problem.php?id=3676 回文自动机模板题 4年前的APIO如今竟沦为模板,,,╮(╯▽╰)╭,唉 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 300001 char ss[N]; int s[N]; int tot=1,last; int fail[N],len

bzoj千题计划106:bzoj1014 [JSOI2008]火星人prefix

http://www.lydsy.com/JudgeOnline/problem.php?id=1014 两个后缀的最长公共前缀:二分+hash 带修改带插入:splay维护 #include<cstdio> #include<cstring> #include<iostream> #define L 100001 typedef unsigned long long ULL; using namespace std; char s[L+4]; int tot,root

bzoj千题计划108:bzoj1018: [SHOI2008]堵塞的交通traffic

http://www.lydsy.com/JudgeOnline/problem.php?id=1018 关键点在于只有两行 所以一个2*m矩形连通情况只有6种 编号即对应代码中的a数组 线段树维护 用b数组表示 节点第0/1行的最右一列是否连接了右边 来 辅助 节点的合并 查询 对两个点位于矩形的位置分4种情况讨论 两点是否联通,要考虑四种情况 (以两个位置是矩形左上角和右上角为例) 1.直接联通,线段树的节点包含了这种情况,直接判断 2. 3. 4. 后三种情况需要再查询[1,l]和[r,n

bzoj千题计划109:bzoj1019: [SHOI2008]汉诺塔

http://www.lydsy.com/JudgeOnline/problem.php?id=1019 题目中问步骤数,没说最少 可以大胆猜测移动方案唯一 (真的是唯一但不会证) 设f[i][j] 表示 从i号柱子 上把j个盘子移到 g[i][j] 柱子上的步数 初始化:f[0][1]=1,g[0][1] 根据优先级决定 设三根柱子分别为0,1,2 对于每一个f[x][i], 把前i-1个移走,把第i个移走,把前i-1个移回 令y=g[x][i-1],则k=0+1+2-x-y 我们希望 把i-

bzoj千题计划111:bzoj1021: [SHOI2008]Debt 循环的债务

http://www.lydsy.com/JudgeOnline/problem.php?id=1021 如果A收到了B的1张10元,那么A绝对不会把这张10元再给C 因为这样不如B直接给C优 由此可以推出 若A欠B20元,B欠C 30元, 那么A还C20元,B还C10元最优 所以一共只有 A->BC   B->AC  C->AB AB->C  BC->A  AC->B 这6种转移情况 根据输入,我们可以知道三人最终手中有多少钱ea.eb.ec,一共有多少钱sum 设f

bzoj千题计划112:bzoj1022: [SHOI2008]小约翰的游戏John

http://www.lydsy.com/JudgeOnline/problem.php?id=1022 http://www.cnblogs.com/TheRoadToTheGold/p/6744825.html #include<cstdio> #include<iostream> using namespace std; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar();

bzoj千题计划113:bzoj1023: [SHOI2008]cactus仙人掌图

http://www.lydsy.com/JudgeOnline/problem.php?id=1023 dp[x] 表示以x为端点的最长链 子节点与x不在同一个环上,那就是两条最长半链长度 子节点与x在同一个环上,环形DP,单调队列优化 对于每一个环,深度最小的那个点 有可能会更新 上层节点, 所以 每一个环DP完之后,更新 dp[深度最小的点] #include<cstdio> #include<iostream> #include<algorithm> using