JZOJ 3506. 【NOIP2013模拟11.4A组】善良的精灵

来源:互联网 发布:mac 流程图软件 免费 编辑:程序博客网 时间:2024/06/04 18:43

Description

从前有一个善良的精灵。

一天,一个年轻人B找到她并请他预言他的未来。这个精灵透过他的水晶球看到这个年轻人将要遇见一个非常美丽的公主并要娶她。。精灵在一张纸上画了N个点并用一些线段将他们连起来,每条线段两端连着不同的点。画完了之后,精灵让年轻人去除一条线段。然后精灵尝试将每个点用红色或者蓝色进行染色,同时使得那里没有一条线段的两端是相同的颜色。如果精灵能够成功染色,这个预言就能成真。

年轻人想要遇见那位公主,因此他请求你去帮助他,找到所有的删除之后能对图进行成功染色的线段。

Input

输入文件中的第一行为两个整数N,M,分别表示点的个数和线段的条数。

接下来的M行,描述了每条线段,每行有两个整数v,u(1<=v,u<=n),表示线段两端的点的标号。注意:没有线段会重复出现。

Output

输出文件中的第一行为一个整数K,即删除之后能对点进行染色的线段的个数。

接下来的一行包含K个从小到大的数字,数字之间用一个空格隔开,行末没有空格,表示满足条件的线段的标号。每条线段的标号为读入顺序的标号。

Sample Input

输入1:

4 4

1 2

1 3

2 4

3 4

输入2:

4 5

1 2

2 3

3 4

4 1

1 3

Sample Output

输出1:

4

1 2 3 4

输出2:

1

5

Data Constraint

对于50%的数据,满足:N,M<=1000;

对于100%的数据,满足:N,M<=10000。

Solution

  • 首先,设 fodd[i]feven[i]godd[x]geven[x]sum

  • fodd[i] 表示 编号为 i 的边 被多少个 奇数环 包含;

  • feven[i] 表示 编号为 i 的边 被多少个 偶数环 包含;

  • godd[x] 表示 编号为 x 的点 的奇数环标记;

  • geven[i] 表示 编号为 x 的点 的偶数环标记;

  • sum 表示奇数环的总个数。(意义用法稍后讲解)

  • 众所周知,如果有奇数环就要果断删边,偶数环则应保留。

  • 所以说,一条边 i 可以被删,当且仅当 fodd[i]=sum 并且 feven[i]=0

  • 即:被所有奇数环包含,没有偶数环经过。

  • 那么问题就转换成如何在线性时间复杂度内处理出 F 数组。

  • 考虑把图当做树来看,处理出深度数组 dep[i]

  • 若当前点为 u ,连向 v ,而 dep[v] 有值且小于 dep[u] ,则说明出现了一个环。

  • 那么用 dep[u]dep[v] (环内其他点的个数) 的奇偶性可以知道这是一个奇数环还是偶数环。

  • 之后累加 fodd/even[i],可以则累加 sum ,接着:(关键!

  • godd/even[u]++godd/even[v] ,即打一个 Tag 标记(类似差分约束),如下图:

Solution

  • 那么如果 sum=0 (没有奇数环)就所有边都可以删,全部输出啦!

  • 紧接着,再做一遍 DFS ,像统计子节点个数一样从下至上累加 Tag 标记;

  • 中途更改 F 数组即可,注意奇偶对应。如下图:

Solution

  • 那么计算答案即可!

Code

#include<cstdio>using namespace std;const int N=10001;int tot,sum;int first[N],next[N*2],en[N*2],w[N*2];int fodd[N],feven[N],godd[N],geven[N];int ans[N],dep[N];bool bz[N*2],vis[N];inline int read(){    int data=0; char ch=0;    while(ch<'0' || ch>'9') ch=getchar();    while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();    return data;}inline void insert(int x,int y,int z){    next[++tot]=first[x];    first[x]=tot;    en[tot]=y;    w[tot]=z;}inline void dfs(int x,int fa){    dep[x]=dep[fa]+1;    for(int i=first[x];i;i=next[i])        if(en[i]!=fa)        {            if(!dep[en[i]])            {                bz[i]=true;                dfs(en[i],x);            }else                if(dep[en[i]]<dep[x])//Link                    if((dep[x]-dep[en[i]])&1)                    {                        geven[en[i]]--;                        geven[x]++;                        feven[w[i]]=1;//Even                    }else                    {                        godd[en[i]]--;                        godd[x]++;                        fodd[w[i]]=1;//Odd                        sum++;                    }        }}inline void find(int x){    vis[x]=true;    for(int i=first[x];i;i=next[i])        if(bz[i])        {            find(en[i]);            fodd[w[i]]=godd[en[i]];            feven[w[i]]=geven[en[i]];            godd[x]+=godd[en[i]];            geven[x]+=geven[en[i]];        }}int main(){    int n=read(),m=read();    for(int i=1;i<=m;i++)    {        int x=read(),y=read();        insert(x,y,i);        insert(y,x,i);    }    for(int i=1;i<=n;i++)        if(!dep[i]) dfs(i,0);    if(!sum)    {        printf("%d\n",m);        for(int i=1;i<=m;i++) printf("%d ",i);        return 0;    }    for(int i=1;i<=n;i++)        if(!vis[i]) find(i);    for(int i=1;i<=m;i++)        if(!feven[i] && fodd[i]==sum) ans[++ans[0]]=i;    printf("%d\n",ans[0]);    for(int i=1;i<=ans[0];i++) printf("%d ",ans[i]);    return 0;}
2 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 做完鼻子感冒了怎么办 鼻子不通流鼻涕打喷嚏怎么办 一岁半宝宝流鼻涕鼻塞怎么办 没有感昌流鼻涕怎么办 婴儿感冒咳嗽流鼻涕怎么办 小孩经常流鼻子怎么办 5岁宝宝流鼻涕怎么办 小孩鼻涕一直流怎么办 二岁宝宝流鼻涕怎么办 小婴儿有点鼻塞怎么办 宝宝流鼻涕总不好怎么办 孩子鼻炎睡不好怎么办 鼻炎清鼻涕不止怎么办 宝宝持续低烧流鼻涕怎么办 孩子鼻塞不通气怎么办 2月婴儿感冒怎么办 长期流黄鼻涕怎么办 孩子流清水鼻涕怎么办 小孩有点流鼻子怎么办 初生婴儿堵鼻子怎么办? 小孩反复发烧了怎么办 小孩突然发烧了怎么办 40天宝宝鼻塞怎么办 宝宝伤风鼻子不通怎么办 鼻子伤风不通气怎么办 宝宝伤风流鼻子怎么办 十个月婴儿上火怎么办 一个多月宝宝鼻子有鼻屎怎么办 三个月婴儿感冒发烧怎么办 小孩感冒发烧流鼻涕怎么办 小孩感冒发烧反反复复怎么办 宝宝反复发烧39怎么办 一岁婴儿流鼻涕怎么办 四岁宝宝发烧怎么办 小孩流清鼻涕怎么办? 5宝宝光流清鼻涕怎么办 孩子一直流鼻子怎么办 10岁天天流鼻涕怎么办 喉咙痛又痒咳嗽怎么办 60天宝宝流鼻涕怎么办 宝宝流鼻子严重怎么办