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]);
    }
    
  • 相关阅读:
    第二十九课 循环链表的实现
    第二十八课 再论智能指针(下)
    第二十七课 再论智能指针(上)
    第二十六课 典型问题分析(Bugfix)
    普通new和placement new的重载
    leetcode 581. Shortest Unsorted Continuous Subarray
    leetcode 605. Can Place Flowers
    leetcode 219. Contains Duplicate II
    leetcode 283. Move Zeroes
    leetcode 217. Contains Duplicate
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14727266.html
Copyright © 2011-2022 走看看