阅读 120

CF1519E Off by One

CF1519E Off by One

一、题目

点此看题

二、解法

看到这个题就感觉很像匹配,我们把这个题先转化成图论模型。

我是这样转化的:先把坐标上的点按两个方向都移动一下,算出 x,yx,y 的比值,然后比值相同的点可以连边。但仔细想一想这不是一般图最大匹配吗?做得动就有鬼了

你需要知道:匹配点是不容易的,但是匹配边是容易的。我们不妨把上图点和边的意义互换一下,我们把 x,yx,y 的比值当成点,每个坐标点就代表了连接该图两个点的边,那么每次我们可以匹配两个具有共同点的边。

可以构造给出最优解,答案的上界是每个联通块的边数除 22 下取整求和,考虑构造到这个上界。构造可以考虑 dfsdfs 树,对于当前点 uu 如果已有的边数是奇数的话我们把父边划分给当前点,否则把父边留给父亲,dfsdfs 数是不存在交叉边的,返祖边可以全部留给祖先。

时间复杂度 O(n)O(n)

#include <cstdio>#include <vector>#include <iostream>#include <map>using namespace std;const int M = 400005;#define make make_pair#define ll long long#define pll pair<ll,ll>int read(){	int x=0,f=1;char c;	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}	return x*f;
}int n,m,res,d[M];vector<int> g[M],h[M],ans[M];map<pll,int> mp;ll gcd(ll a,ll b){	return !b?a:gcd(b,a%b);
}pll get(ll a,ll b,ll c,ll d){
	ll g=gcd(a*d,c*b);	return make(a*d/g,c*b/g);
}void dfs(int u,int fa){
	d[u]=d[fa]+1;int tmp=-1;	for(int i=0;i<g[u].size();i++)
	{		int v=g[u][i];		if(d[v])
		{			if(d[v]>d[u]) continue;			if(v==fa && tmp==-1) tmp=i;			else ans[v].push_back(h[u][i]);
		}		else dfs(v,u);
	}	if(fa)
	{		if(ans[u].size()&1) ans[u].push_back(h[u][tmp]);		else ans[fa].push_back(h[u][tmp]);
	}
}signed main(){
	n=read();	for(int i=1;i<=n;i++)
	{		int a=read(),b=read(),c=read(),d=read();		int &x=mp[get(a+b,b,c,d)],&y=mp[get(a,b,c+d,d)];		if(!x) x=++m;if(!y) y=++m;
		g[x].push_back(y);g[y].push_back(x);
		h[x].push_back(i);h[y].push_back(i);
	}	for(int i=1;i<=m;i++)		if(!d[i]) dfs(i,0);	for(int i=1;i<=m;i++)
		res+=ans[i].size()/2;	printf("%d\n",res);	for(int i=1;i<=m;i++)		for(int j=1;j<ans[i].size();j+=2)			printf("%d %d\n",ans[i][j-1],ans[i][j]);
}


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐