zoukankan      html  css  js  c++  java
  • @codeforces


    @description@

    给定一个 n 点 m 边的图(n, m<=10^6),记第 i 个点的度数为 di。

    现让你保留不超过 (n + m) / 2(向上取整)条边,并且要求新图中第 i 个点的度数 di' 满足 2di' ≥ di。

    不难证明它一定有解。现你只需要输出任意一种方案。

    input
    第一行包含 n 和 m,表示点数与边数。
    接下来 m 行每一行包含 2 个整数 ui, vi,描述一条边。
    无重边、自环。

    output
    第一行首先输出保留的边的数量 k。
    接下来 k 行每行两个整数 u, v,描述你所保留的边。

    sample input
    10 20
    4 3
    6 5
    4 5
    10 8
    4 8
    5 8
    10 4
    9 5
    5 1
    3 8
    1 2
    4 7
    1 4
    10 7
    1 7
    6 1
    9 6
    3 9
    7 9
    6 2
    sample output
    12
    2 1
    4 1
    5 4
    6 5
    7 1
    7 4
    8 3
    8 5
    9 3
    9 6
    10 4
    10 7

    @solution@

    好玄妙的题目啊。。。
    但我想到的实现好像跟标算不大一样,不过用到的算法大致是一样的。

    根据题目,偶数新度数最小为原度数的1/2,奇数新度数最小为(原度数+1)的1/2。
    假如所有点的新度数都取最小值,则偶度点连接的边一半被删除,一半被保留;奇度点连接的某一条边保留,然后转为偶度点的情况。
    这样将点的度数分奇偶讨论的过程,将某一个点连接的边分为相同大小的集合的操作,有没有让你联想到什么。

    欧拉回路。即不重复、不遗漏经历所有边的路径。
    欧拉回路可以将一个偶点连接的边分为“入边”和“出边”两类相同个数的边。
    同时可以发现,以奇点开始奇点结束的欧拉路径,起点恰好多一条出边,终点恰好多一条入边。
    这不和我们刚刚的讨论恰好相一致吗?

    考虑欧拉回路,即没有奇数点的情况。
    如果该欧拉回路长度为偶数,我们只需要隔一条边保留一条边即可,这样可以保证每一个点入边与出边保留恰好一条,且总边数减至一半。
    否则如果长度为奇数,如 (1, 2), (2, 3), (3, 1),我们从第一条边开始隔一条边保留一条边,可以发现最后我们会同时保留第一条与最后一条,而这两条边是相邻的,故我们总边数变为原先的 1/2 再加 1 。
    因为没有重边,长度为奇数必然包含两个以上的点,故最多会加 n/2 次。满足题设。

    考虑欧拉路径,即起点和终点为奇数点的情况。
    如果该欧拉回路长度为奇数,从起点开始隔一条边保留一条边即可。总边数变为原先的 1/2 加 1(因为奇数长度保留的要比删去的多一条)。
    否则如果长度为偶数,如 (1, 2), (2, 3), (3, 4), (4, 5),我们从起点开始隔一条边保留一条边之后,再将与终点连接的边保留。总边数变为原先的 1/2 加 1。
    最多会有 n 个奇数点,每两个奇数点之间产生一个路径,故最多会加 n/2 次。满足题设。

    补充一小点:如何求解多条欧拉路径。你只需要将奇数点两两分组然后连接虚边,跑欧拉回路,然后两个虚边之间夹着的就是一条欧拉路径。如果没有虚边就是一条欧拉回路。

    注意题目中给出的图可能不连通,你需要对每个连通块都进行操作。

    @accepted code@

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int MAXN = 1000000;
    const int MAXM = 5000000;
    struct edge{
    	int from, to;
    	bool flag, tag;
    	edge *nxt, *rev;
    }edges[MAXM + 5], *adj[MAXN + 5], *ecnt = &edges[0];
    int fa[MAXN + 5];
    int find(int x) {
    	return fa[x] = (fa[x] == x ? x : find(fa[x]));
    }
    void addedge(int u, int v, bool t) {
    	edge *p = (++ecnt), *q = (++ecnt);
    	p->from = u, p->to = v, p->nxt = adj[u], adj[u] = p;
    	q->from = v, q->to = u, q->nxt = adj[v], adj[v] = q;
    	p->flag = q->flag = false, p->tag = q->tag = t;
    	p->rev = q, q->rev = p;
    	if( find(u) != find(v) )
    		fa[find(u)] = find(v);
    }
    int deg[MAXN + 5]; edge *e[MAXM + 5], *tmp[MAXM + 5];
    int ans1[MAXM + 5], ans2[MAXM + 5];
    int cnt, tot;
    void dfs(int x) {
    	for(edge *p=adj[x];p;p=adj[x]) {
    		if( p->flag ) {
    			adj[x] = adj[x]->nxt;
    			continue;
    		}
    		p->flag = p->rev->flag = true;
    		adj[x] = adj[x]->nxt; dfs(p->to); e[++cnt] = p;
    	}
    }
    void print() {
    	printf("%d
    ", tot);
    	for(int i=1;i<=tot;i++)
    		printf("%d %d
    ", ans1[i], ans2[i]);
    }
    int main() {
    	int n, m; scanf("%d%d", &n, &m);
    	for(int i=1;i<=n;i++)
    		fa[i] = i;
    	for(int i=1;i<=m;i++) {
    		int u, v; scanf("%d%d", &u, &v);
    		addedge(u, v, true); deg[u]++, deg[v]++;
    	}
    	int lst = 0, fir = 0;
    	for(int i=1;i<=n;i++) {
    		if( deg[i] & 1 ) {
    			if( !lst ) lst = i;
    			else addedge(lst, i, false), lst = 0;
    		}
    	}
    	for(int i=1;i<=n;i++) {
    		if( find(i) != i ) continue;
    		cnt = lst = fir = 0; dfs(i);
    		for(int j=1;j<=cnt;j++) {
    			if( !e[j]->tag ) {
    				if( lst ) {
    					int siz = 0;
    					for(int k=lst+1;k<=j-1;k++)
    						tmp[++siz] = e[k];
    					if( siz && siz % 2 == 0 ) tmp[siz+1] = tmp[siz], siz++;
    					for(int k=1;k<=siz;k+=2)
    						tot++, ans1[tot] = tmp[k]->from, ans2[tot] = tmp[k]->to;
    					if( siz && siz % 2 == 0 )
    						tot++, ans1[tot] = tmp[siz]->from, ans2[tot] = tmp[siz]->to;
    				}
    				else fir = j;
    				lst = j;
    			}
    		}
    		if( !lst ) {
    			for(int j=1;j<=cnt;j+=2)
    				tot++, ans1[tot] = e[j]->from, ans2[tot] = e[j]->to;
    		}
    		else {
    			int siz = 0;
    			for(int j=lst+1;j<=cnt;j++)
    				tmp[++siz] = e[j];
    			for(int j=1;j<=fir-1;j++)
    				tmp[++siz] = e[j];
    			if( siz && siz % 2 == 0 ) tmp[siz+1] = tmp[siz], siz++;
    			for(int j=1;j<=siz;j+=2)
    				tot++, ans1[tot] = tmp[j]->from, ans2[tot] = tmp[j]->to;
    		}
    	}
    	print();
    }
    

    @details@

    虽然题解里说的好像非常自然。
    不过要是在做比赛的时候能想得到才有鬼好吧。

    求欧拉路的时候通过打 tag 标记访问过的边会 TLE。需要类比网络流中的当前弧优化,将访问过的边直接删除。

  • 相关阅读:
    IOS中多版本,多设备类型支持注意事项
    runtime
    网络搜集各种iOS开源类库
    Foudation框架常用结构体和常用类
    iOS开发-正则表达式的使用方法
    Github上的iOS资料-个人记录
    iOS应用程序多语言本地化
    星期判断
    【设定本地通知为周一到周五提醒, 周末不提醒解决办法】
    iOS9 HTTPS解决办法
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11114236.html
Copyright © 2011-2022 走看看