Va爷的胡策题T2

E. Fairy

time limit per test1.5 seconds

memory limit per test256 megabytes

inputstandard input

outputstandard output

Once upon a time there lived a good fairy A. One day a fine young man B came to her and asked to predict his future. The fairy looked into her magic ball and said that soon the fine young man will meet the most beautiful princess ever and will marry her. Then she drew on a sheet of paper n points and joined some of them with segments, each of the segments starts in some point and ends in some other point. Having drawn that picture, she asked the young man to erase one of the segments from the sheet. Then she tries to colour each point red or blue so, that there is no segment having points of the same colour as its ends. If she manages to do so, the prediction will come true. B wants to meet the most beautiful princess, that’s why he asks you to help him. Find all the segments that will help him to meet the princess.

Input

The first input line contains two integer numbers: n — amount of the drawn points and m — amount of the drawn segments (1 ≤ n ≤ 104, 0 ≤ m ≤ 104). The following m lines contain the descriptions of the segments. Each description contains two different space-separated integer numbers v, u (1 ≤ v ≤ n, 1 ≤ u ≤ n) — indexes of the points, joined by this segment. No segment is met in the description twice.

Output

In the first line output number k — amount of the segments in the answer. In the second line output k space-separated numbers — indexes of these segments in ascending order. Each index should be output only once. Segments are numbered from 1 in the input order.

Examples

input

4 4

1 2

1 3

2 4

3 4

output

4

1 2 3 4

input

4 5

1 2

2 3

3 4

4 1

1 3

output

1

5

好的下面是Vampire学长的题面:

10%-----随便暴力没写过。。

40%-----暴力枚举要删的边,然后对剩余的图进行黑白染色然后判断是否为二分图。(现场打的暴力,本来想写LCT的,写惨辣!)

测试暴力代码:

具体的判断是否为二分图的方法大概就是染色后看相邻点的颜色是否一样.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define maxn 1000010
int read()
{
int x=0,f=1; char ch=getchar();
while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
int n,m,ans=0;
int an[maxn];
struct data{int id,from,to;}road[maxn];
struct dat{int to,next;}edge[maxn];
int head[maxn],cnt=1;
//判断是否为二分图
void add(int u,int v)
{
cnt++;
edge[cnt].next=head[u]; head[u]=cnt;
edge[cnt].to=v;
}
void insert(int u,int v)
{
add(u,v); add(v,u);
}
int color[maxn];
bool bfs(int s)
{
queue<int>q;
q.push(s);
while (!q.empty())
{
int now=q.front();q.pop();
for (int i=head[now]; i; i=edge[i].next)
if (edge[i].to==0) continue; else
if (color[edge[i].to]==-1)
{
q.push(edge[i].to);
color[edge[i].to]=!color[now];
}
else
if (color[edge[i].to]==color[now])
return false;
}
return true;
}
bool pd()
{
memset(color,-1,sizeof(color));
bool f=0;
for (int i=1; i<=n; i++)
if(color[i]==-1 && !bfs(i)) {f=1; break;}
if (f==0) return true;
else return false;
}
//主程序读入和处理
int main()
{
freopen("fairy.in","r",stdin);
freopen("fairy.out","w",stdout);
n=read(),m=read();
for (int i=1; i<=m; i++)
road[i].from=read(),road[i].to=read(),road[i].id=i,
insert(road[i].from,road[i].to);
for (int i=1; i<=m; i++)
{
edge[i*2].to=0;edge[i*2+1].to=0;
bool f=pd();
edge[i*2].to=road[i].to;edge[i*2+1].to=road[i].from;
if (f) an[++ans]=i;
}
printf("%d\n",ans);
for (int i=1; i<=ans; i++)
printf("%d ",an[i]);
fclose(stdin);
fclose(stdout);
return 0;
}
70%-----测试时Va爷把100%改成10^6级辣...于是本来可以艹DP的LCT会被卡常3组...气死CA爷了...

CA爷当时的LCT:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 300010
#define GET (ch>='0'&&ch<='9')
#define MAXINT 0x3f3f3f3f
#define is_root(x) (tree[tree[x].fa].ch[0]!=x&&tree[tree[x].fa].ch[1]!=x)
using namespace std;
int n,m,T,Top,cnt;
int ans[MAXN<<1];
int sta[MAXN<<1],top;
int In[MAXN<<1],on[MAXN<<1];
struct splay
{
int ch[2],fa,minn,st,sum,val;
bool rev;
}tree[MAXN<<1];
inline void in(int &x)
{
char ch=getchar();x=0;
while (!GET) ch=getchar();
while (GET) x=x*10+ch-'0',ch=getchar();
}
struct edge { int u,v,w; }e[MAXN];
struct Edge
{
int to;
Edge *next;
}E[MAXN<<1],*prev1[MAXN],*prev2[MAXN];
void insert1(int u,int v) { E[++Top].to=v;E[Top].next=prev1[u];prev1[u]=&E[Top]; }
void insert2(int u,int v) { E[++Top].to=v;E[Top].next=prev2[u];prev2[u]=&E[Top]; }
void push_down(int x)
{
if (tree[x].rev)
{
tree[tree[x].ch[0]].rev^=1,tree[tree[x].ch[1]].rev^=1;
swap(tree[x].ch[0],tree[x].ch[1]);tree[x].rev^=1;
}
}
void push_up(int x)
{
tree[x].minn=tree[x].val;tree[x].st=x;tree[x].sum=x>n;
if (tree[x].ch[0])
{
if (tree[tree[x].ch[0]].minn<tree[x].minn) tree[x].minn=tree[tree[x].ch[0]].minn,tree[x].st=tree[tree[x].ch[0]].st;
tree[x].sum+=tree[tree[x].ch[0]].sum;
}
if (tree[x].ch[1])
{
if (tree[tree[x].ch[1]].minn<tree[x].minn) tree[x].minn=tree[tree[x].ch[1]].minn,tree[x].st=tree[tree[x].ch[1]].st;
tree[x].sum+=tree[tree[x].ch[1]].sum;
}
}
void rot(int x)
{
int y=tree[x].fa,z=tree[y].fa,l,r;
l=(tree[y].ch[1]==x);r=l^1;
if (!is_root(y)) tree[z].ch[tree[z].ch[1]==y]=x;
tree[tree[x].ch[r]].fa=y;tree[y].fa=x;tree[x].fa=z;
tree[y].ch[l]=tree[x].ch[r];tree[x].ch[r]=y;
push_up(y);push_up(x);
}
void Splay(int x)
{
top=0;sta[++top]=x;
for (int i=x;!is_root(i);i=tree[i].fa) sta[++top]=tree[i].fa;
while (top) push_down(sta[top--]);
while (!is_root(x))
{
int y=tree[x].fa,z=tree[y].fa;
if (!is_root(y))
{
if ((tree[y].ch[0]==x)^(tree[z].ch[0]==y)) rot(x);
else rot(y);
}
rot(x);
}
}
void access(int x) { for (int i=0;x;i=x,x=tree[x].fa) Splay(x),tree[x].ch[1]=i,push_up(x); }
void make_root(int x) { access(x);Splay(x);tree[x].rev^=1; }
void link(int x,int y) { make_root(x);tree[x].fa=y; }
void cut(int x,int y) { make_root(x);access(y);Splay(y);tree[y].ch[0]=tree[x].fa=0;push_up(y); }
void split(int x,int y) { make_root(x);access(y);Splay(y); }
int find_root(int x) { for (access(x),Splay(x);tree[x].ch[0];x=tree[x].ch[0]); return x; }
void ins(int x)
{
int u=e[x].u,v=e[x].v;
if (u==v) { In[x]=1;cnt++;return; }
if (find_root(u)!=find_root(v)) on[x]=1,link(u,x+n),link(v,x+n);
else
{
split(u,v);int y=tree[v].st-n;
if (e[y].w<e[x].w)
{
if (tree[v].sum&1^1) In[y]=1,cnt++;
cut(e[y].u,y+n);cut(e[y].v,y+n);link(u,x+n);link(v,x+n);
on[y]=0;on[x]=1;
}
else
if (tree[v].sum&1^1) In[x]=1,cnt++;
}
}
void del(int x)
{
if (on[x]) cut(e[x].u,x+n),cut(e[x].v,x+n);
else if (In[x]) cnt--;
}
int main()
{
in(n);in(m);int s,t;
for (int i=1;i<=n;i++) tree[i].val=tree[i].minn=MAXINT,tree[i].st=i;
for (int i=1;i<=m;i++)
{
in(e[i].u);in(e[i].v);
s=0;t=i;e[i].w=t;
insert1(s,i);insert2(t,i);
tree[i+n].val=tree[i+n].minn=t;tree[i+n].st=i+n;tree[i+n].sum=1; e[i+m].u=e[i].u;e[i+m].v=e[i].v;
s=i+1;t=m+2;e[i+m].w=t;
insert1(s,i+m);insert2(t,i+m);
tree[i+m+n].val=tree[i+m+n].minn=t;tree[i+m+n].st=i+m+n;tree[i+m+n].sum=1;
}
for (int x=0;x<=m+1;x++)
{
for (Edge *i=prev1[x];i;i=i->next) ins(i->to);
for (Edge *i=prev2[x];i;i=i->next) del(i->to);
if (!cnt) { for (Edge *i=prev2[x];i;i=i->next) ans[++ans[0]]=i->to; }
}
sort(ans+1,ans+ans[0]+1);printf("%d\n",ans[0]);
for (int i=1;i<=ans[0];i++) printf(i==ans[0]?"%d\n":"%d ",ans[i]);
}
100%-----DP+dfs找环;

题解:

首先我们可以知道一个图是二分图的充要条件是这个图不存在奇环

所以就可以分成三种情况来讨论一下:

①:图中没有奇环,此时删除图中 任何一条边 都满足条件。

②:图中有一个奇环,此时删除 奇环上的任何一条边 都满足条件。

③:图中有超过一个奇环,此时删除的边必须满足它是 所有奇环的公共边。

那么现在我们对这个图建出一棵 DFS 树,在 DFS 树中, 存在从当前点连向祖先的边,我们将它记为返祖边 (图中红色边)。



比较显然的是,返祖边 (a,b) 和 树上路径 (b,a) 可以在图中形成一个环。

如图 3→1→2→3 形成了一个奇环,4→1→2→3→4 形成了一个偶环。

那么这个问题的思路就比较清晰了,首先求出只包含一条返祖边的奇环的个数。

不妨将只包含一条返祖边的环记为 单祖环,很显然有:

①:如果图中没有单祖奇环,那么删除图中 任何一条边 都满足条件。

②:如果图中有单祖奇环,那么删除的边必须满足是 所有单祖奇环的公共边。

现在只讨论图中有单祖奇环的情况

删除返祖边能否满足条件

①:如果只有一个单祖奇环, 那么删除单祖奇环中的返祖边一定满足条件。

②:如果有超过一个单祖奇环, 那么删除任何一条返祖边都不满足条件。(返祖边不是所有单祖奇环的公共边)

删除树边能否满足条件

此时只考虑树边是否能被所有单祖奇环经过,那么如果 (a,b) 是返祖边,树上的链 (b,a) 都在环上。

如果树边在 所有的 单祖奇环上又 不在任何一个单祖偶环上 那么删去这条边是满足条件的。

为什么还要看偶环呢?



一个很显然的奇环是 5→1→2→5, 这个奇环只包含一条返祖边 (5,1)。

但是这个图只有删去 (2,5) 或 (1,5)可以使得图中没有奇环。

(如果删去 (1,2) ,图中还是存在奇环 6→1→5→2→4→6,这个奇环包含两条返祖边)

你可能已经发现了,在之前我们只考虑了只包含一条返祖边的奇环,而事实上奇环也可能包含两条返祖边。

如果边 (a,b) 在单祖奇环 X 中又在单祖偶环 Y中, 那么即使删除这条边,X 和 Y 仍然能形成一个奇环。

所以判断删除树边能否满足条件是需要判断单祖偶环的。

这样我们就dp求一下奇环和偶环就好了

设 F[i] 为点 i 的父边被多少个单祖奇环包含,G[i] 为点 i 的父边被多少个单祖偶环包含。

在 DFS树 中自底向上 DP。

考虑点 u,对于边 (u, v),有三种情况:

①:(u, v) 是树边,点 u 是 v 的父亲,F[u] += F[v], G[u] += G[v]

②:(u, v) 是一条返祖边, F[u]++ 或 G[u]++;

③:(v, u) 是一条返祖边, F[u]- - 或 G[u]- -;

时间复杂度:O(n+m)

于是A之……

改后AC代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int read()
{
int x=0,f=1; char ch=getchar();
while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
#define maxn 1000010*2
int n,m;
struct data{int to,next;}edge[maxn];
struct dat{int u,v;int o,e;}road[maxn];
bool visit[maxn]={0};
int head[maxn],cnt=1;
int h[maxn],ont[maxn];
int odd[maxn],even[maxn];
int ans[maxn]; void add(int u,int v)
{
cnt++; edge[cnt].to=v;
edge[cnt].next=head[u]; head[u]=cnt;
}
void insert(int u,int v)
{
add(u,v); add(v,u);
} void dfs(int now,int deep)
{
h[now]=deep; visit[now]=1; int vt;
for (int i=head[now]; i; i=edge[i].next)
if (!visit[vt=edge[i].to])
ont[i>>1]=1,dfs(vt,deep+1);
} void work(int now)
{
visit[now]=1; int vt;
for (int i=head[now]; i; i=edge[i].next)
if (ont[i>>1] && !visit[vt=edge[i].to])
{
work(vt);
odd[now]+=odd[vt];
even[now]+=even[vt];
road[i>>1].o=odd[vt];
road[i>>1].e=even[vt];
}
} void debug()
{
for (int i=1; i<=m; i++) printf("%d ",odd[i]);
puts("");
for (int j=1; j<=m; j++) printf("%d ",even[j]);
for (int i=1; i<=m; i++) printf("%d %d\n",road[i].o,road[i].e);
puts("");
} int main()
{
n=read(),m=read();
for (int i=1; i<=m; i++)
{
int u=read(),v=read();
road[i].u=u,road[i].v=v;
road[i].o=road[i].e=0;
insert(u,v);
}
for (int i=1; i<=n; i++)
if (!visit[i]) dfs(i,1);
int tot=0;
for (int i=1; i<=m; i++)
if (!ont[i])
{
int u=road[i].u,v=road[i].v;
if (h[u]>h[v]) swap(u,v);
if ((h[u]-h[v])&1) odd[u]--,odd[v]++;
else even[u]--,even[v]++,tot++,road[i].e=1;
}
if (!tot)
{
printf("%d\n",m);
for (int i=1; i<=m; i++)
if (i!=m)
printf("%d ",i);
else
printf("%d\n",i);
return 0;
}
memset(visit,0,sizeof(visit));
for (int i=1; i<=n; i++)
if (!visit[i]) work(i);
int num=0;
for (int i=1; i<=m; i++)
{
if (!ont[i])
{ if (tot==1 && road[i].e) ans[++num]=i;}
else if (!road[i].o && road[i].e==tot) ans[++num]=i;
}
printf("%d\n",num);
for (int i=1; i<=num; i++)
if (i!=num)
printf("%d ",ans[i]);
else
printf("%d\n",ans[i]);
return 0;
}

BZOJ-4424 &&CodeForces-19E Fairy DP+dfs (Link-Cut-Tree可A)的更多相关文章

  1. Codeforces Round #339 (Div. 2) A. Link/Cut Tree 水题

    A. Link/Cut Tree 题目连接: http://www.codeforces.com/contest/614/problem/A Description Programmer Rostis ...

  2. [BZOJ 2002] [HNOI2010]弹飞绵羊(Link Cut Tree)

    [BZOJ 2002] [HNOI2010]弹飞绵羊(Link Cut Tree) 题面 某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏.游戏一 ...

  3. [CodeForces - 614A] A - Link/Cut Tree

    A - Link/Cut Tree Programmer Rostislav got seriously interested in the Link/Cut Tree data structure, ...

  4. link cut tree 入门

    鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构. link cut tree:研究popoqqq那个神ppt. bzoj1036:维护access操作就可以了. ...

  5. Link Cut Tree学习笔记

    从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...

  6. Link Cut Tree 总结

    Link-Cut-Tree Tags:数据结构 ##更好阅读体验:https://www.zybuluo.com/xzyxzy/note/1027479 一.概述 \(LCT\),动态树的一种,又可以 ...

  7. LuoguP3690 【模板】Link Cut Tree (动态树) LCT模板

    P3690 [模板]Link Cut Tree (动态树) 题目背景 动态树 题目描述 给定n个点以及每个点的权值,要你处理接下来的m个操作.操作有4种.操作从0到3编号.点从1到n编号. 0:后接两 ...

  8. Link/cut Tree

    Link/cut Tree 一棵link/cut tree是一种用以表示一个森林,一个有根树集合的数据结构.它提供以下操作: 向森林中加入一棵只有一个点的树. 将一个点及其子树从其所在的树上断开. 将 ...

  9. 洛谷P3690 Link Cut Tree (模板)

    Link Cut Tree 刚开始写了个指针版..调了一天然后放弃了.. 最后还是学了黄学长的板子!! #include <bits/stdc++.h> #define INF 0x3f3 ...

  10. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

随机推荐

  1. POJ 2823 Sliding Window 再探单调队列

    重新刷这个经典题,感觉跟以前不一样了,变得更加容易理解了,不讲解了,看代码.注意:要用C++提交,用G++会超时.. 代码: #include <iostream> #include &l ...

  2. Hibernate Java、Hibernate、SQL 之间数据类型转换

    Hibernate映射类型 Java类型 标准SQL类型  integer  java.lang.Integer  integer  long  java.lang.Long  bigint  sho ...

  3. 层叠上下文(The stacking context)

    MDNThe stacking context 层叠上下文是HTML元素的三维概念,这些HTML元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的z轴上延伸,HTML元素依据其自身属性按照优 ...

  4. 022医疗项目-模块二:药品目录的导入导出-对XSSF导出excel类进行封装

    资源全部来源于传智播客. 好的架构师写的程序,就算给刚入门的新手看,新手一看就知道怎么去用.所以我们要对XSSF导出excel类进行封装.这是架构师的工作,但我们也要知道. 我们写一个封装类: 这个类 ...

  5. java String.getBytes()编码问题——String.getBytes(charset)

    String的getBytes()方法是得到一个字串的字节数组,这是众所周知的.但特别要注意的是,本方法将返回该操作系统默认的编码格式的字节数组.如果你在使用这个方法时不考虑到这一点,你会发现在一个平 ...

  6. java内部类 2016年12月13号

    1.在外部类的任意位置创建内部类对象的方法: 1)从外部类的非静态方法之外的任意位置创建某个内部类的对象,必须指明这个对象所在的外部类和内部类:OuterClassName.InnerClassNam ...

  7. 怎么写针对IE9的CSS

    (自己亲自试过有用)针对IE9的CSS只需在相应CSS代码加入只有IE9识别的 \9\0.具体代码如下: .div{ background-color:#0f0\9\0;/* ie9 */ } 其他浏 ...

  8. P2P NAT检测和穿越方式

    一.      NAT类型 本文转自:http://www.cnblogs.com/hummersofdie/archive/2013/05/21/3090163.html  1.基本的NAT类型:只 ...

  9. System.Web.HttpRequestValidationException——从客户端检测到危险的Request值

    这是比较常见的问题了,如果Web表单中有输入类似于Html标签之类的文本,在通过Request.QueryString或者Request.Form传递这些值的时候,就会触发这样的异常,出于脚本注入等安 ...

  10. 在c++程序中执行DOS命令

    转自博客:http://blog.csdn.net/ypist/article/details/8485049 #1,system()方式 在C盘根目录下新建文件夹,名称为12: system(&qu ...