zoukankan      html  css  js  c++  java
  • CF1519E Off by One

    一、题目

    点此看题

    二、解法

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

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

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

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

    时间复杂度 (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
    ",res);
    	for(int i=1;i<=m;i++)
    		for(int j=1;j<ans[i].size();j+=2)
    			printf("%d %d
    ",ans[i][j-1],ans[i][j]);
    }
    
  • 相关阅读:
    python的模块future用法实例解析
    strcmp函数和memcmp函数的用法区别及联系
    esp8266 smartconfig-智能配网分析和使用及注意事项
    ubuntu 18.04 安装并配置adb
    Markdown的常用方法总结
    mac下使用minicom几个注意事项
    最强Linux shell工具Oh My Zsh 指南
    ESP8266源码分析--打印的基本用法
    atom 在Ubuntu 18.04 上安装及基本使用
    ubuntu 查看端口被占用并删除端口
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14727266.html
Copyright © 2011-2022 走看看