Summer training day6 codeforces19E 树链剖分,并查集

来源:互联网 发布:直销软件开发价格 编辑:程序博客网 时间:2024/06/07 00:09

E. Fairy
time limit per test
1.5 seconds
memory limit per test
256 megabytes
input
standard input
output
standard 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 vu (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 41 21 32 43 4
output
41 2 3 4 
input
4 51 22 33 44 11 3
output
15 
题意:给你一幅图(注意可能不是连通的)。然后你从中选取一条边删除,在剩下的图中,所有通过边相连的顶点颜色不能相同。把所有这样的边输出出来。

题解:

我们可以先构造一颗生成树,这颗生成树顶点上的涂色是固定的。然后我们开始处理剩下的边,对于两个顶点颜色相同的边,我们把它放到vectorA中,颜色不同的边放在vectorB当中。如果此时把vectorA中的边加入到生成树中,毫无疑问会产生奇数环,也就是说会产生矛盾。答案所需要的边有一个特点,那就是它一定会被所有的奇数环所包含。如果我们把vectorB中的边加入到生成树中的话,会产生偶数环。如果一条边被偶数环和所有的奇数环所包含,那么它一定不会是答案,因为把他去掉之后会在原来偶数环的基础上产生一个奇数环。

那么我们要找的边就非常清楚了,就是被所有奇数环包含且不被偶数环包含的边。

统计每条边被经过了多少次,借助树链剖分这个数据结构。

我们从vectorA中拿出一条边,把设置这条边的cost为-1。并把u,v链上所有的边都-1操作。

从vectorB中拿出一条比边,把这条边设置cost为1,并把u,v这条链上所有的边都+1操作。

然后统计所有的边,边权值等于vectorB.size()的就是答案。

在构造生成树的时候借助并查集这个数据结构。

代码:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <vector>using namespace std;const int MAXN = 1e5+7;int head[MAXN];int siz[MAXN],fa[MAXN],son[MAXN],dep[MAXN],top[MAXN],w[MAXN];int index;int cnt;int n;struct edge{     int v;     int next;     int cost; }Es[MAXN<<1];  struct segtree{int val[MAXN<<2];int mark[MAXN<<2];void init(){memset(val,0,sizeof(val));memset(mark,0,sizeof(val));}void pushup(int rt){val[rt] = val[rt*2] + val[rt*2+1];}void pushdown(int rt,int l,int r){if(mark[rt]){int mid = (l + r) / 2;val[rt*2] += mark[rt] * (mid - l + 1);val[rt*2+1] += mark[rt] * (r - mid);mark[rt*2] += mark[rt];mark[rt*2+1] += mark[rt];mark[rt] = 0;}}void update(int rt,int l,int r,int ul,int ur,int addval){if(l > ur || r < ul) return ;if(ul <= l && r <= ur) {val[rt] += addval * (r - l + 1);mark[rt] += addval;return ;}pushdown(rt,l,r);int mid = (l+r)/2;update(rt*2,l,mid,ul,ur,addval);update(rt*2+1,mid+1,r,ul,ur,addval);pushup(rt);}int query(int rt,int l,int r,int ul,int ur){if(l > ur || r < ul) return 0;if(ul <= l && r <= ur) {return val[rt];}pushdown(rt,l,r);int mid = (l+r)/2;int a = query(rt*2,l,mid,ul,ur);int b = query(rt*2+1,mid+1,r,ul,ur);return a + b;}}seg;void init(){     index = cnt = 0;     memset(head,-1,sizeof(head));     seg.init();}inline void add_edge(int i,int j,int cost = 1){       Es[cnt].v = j;     Es[cnt].cost = cost;     Es[cnt].next = head[i];     head[i] = cnt++; }   void dfs1(int u,int f,int _dep){son[u] = 0;fa[u] = f;dep[u] = _dep;siz[u] = 1;for(int e = head[u];e != -1;e = Es[e].next){int v = Es[e].v;if(v == fa[u]) continue;dfs1(v,u,_dep+1);siz[u] += siz[v];if(siz[v] > siz[son[u]])son[u] = v;}}void dfs2(int u,int tp){top[u] = tp;w[u] = ++index;if(son[u]) dfs2(son[u],tp);else return ;for(int e = head[u];e != -1;e = Es[e].next){int v = Es[e].v;if(v == son[u] || v == fa[u]) continue;dfs2(v,v);}}int getsum(int u,int v){int ans = 0;int fu = top[u],fv = top[v];while(fu != fv){if(dep[fu] < dep[fv]) /*确保fu在下面*/swap(fu,fv),swap(u,v);ans += seg.query(1,1,n,w[fu],w[u]);u = fa[fu];fu = top[u];}if(u == v) return ans;if(dep[u] < dep[v]) swap(u,v);ans += seg.query(1,1,n,w[son[v]],w[u]);return ans;}void modify(int u,int v,int addval){/*区间修改*/int fu = top[u],fv = top[v];while(fu != fv){if(dep[fu] < dep[fv]) /*确保fu在下面*/swap(fu,fv),swap(u,v);seg.update(1,1,n,w[fu],w[u],addval);u = fa[fu];fu = top[u];}if(u == v) return ;if(dep[u] < dep[v]) swap(u,v);seg.update(1,1,n,w[son[v]],w[u],addval);}int parent[MAXN];int find(int x){return x == parent[x] ? x : parent[x] = find(parent[x]);}void join(int x,int y){int px = find(x);int py = find(y);parent[px] = py;}int U[MAXN],V[MAXN],C[MAXN];int color[MAXN];int cost[MAXN];int m;void dfs(int u,int c){color[u] = c;for(int e = head[u];e != -1;e = Es[e].next){int v = Es[e].v;if(v != fa[u])dfs(v,c ^ 1);}}int main(){init();memset(color,-1,sizeof(color));for(int i = 0;i < MAXN;i++) parent[i] = i;scanf("%d%d",&n,&m);for(int i = 1;i <= m;i++){scanf("%d%d",&U[i],&V[i]); }vector<int> vec;for(int i = 1;i <= m;i++){if(find(U[i]) == find(V[i])){vec.push_back(i);}else{join(U[i],V[i]);add_edge(U[i],V[i]);add_edge(V[i],U[i]);}}for(int i = 1;i <= n;i++){if(!dep[i]) {dfs1(i,i,0);dfs2(i,i);dfs(i,0);}}int tmp = 0;for(int i = 0;i < vec.size();i++){int e = vec[i];if(color[U[e]] == color[V[e]]){modify(U[e],V[e],1);cost[e] = 1;tmp++;}else{modify(U[e],V[e],-10);cost[e] = -1;}}if(tmp == 0) {printf("%d\n",m);for(int i = 1;i <= m;i++){printf("%d ",i);}return 0;}vector<int> ans;for(int i = 1;i <= m;i++){if(cost[i]){if(cost[i] >= tmp) ans.push_back(i);}else if(getsum(U[i],V[i]) >= tmp){ans.push_back(i);}}printf("%d\n",ans.size());for(int i = 0;i < ans.size();i++){printf("%d ",ans[i]);}return 0;}




阅读全文
0 0
原创粉丝点击