• [IOI2020]连接擎天树


    传送门


    IOI的题思维难度确实不小,但是很多题的代码却没有那么难写,也希望这是以后OI以及ACM的出题趋势吧。


    首先两个点之间的路径条数不可能等于3,因为如果两点间有3条不同的路径,那么这个图就一定就会有4条路径,见下图:

    这里能看出1和4之间有3条路径,但2和3之间就有2-1-3,2-1-4-3,2-4-3,2-4-1-3这四条路径。
    那么如果(p[i][j] = 3),直接返回0.


    现在考虑(p[i][j])为1和2的情况。
    对于(p[i][j]=1),就是一棵树;
    对于(p[i][j]=2),就是一个环,或者是一个基环树,但是要保证(i)(j)在环上或者在不同的两棵树上。


    那么思路就有了:
    1.先判断如果有(p[i][j]=3),直接返回0;
    2.否则dfs找到每一个连通块,如果一个连通块内有(p[i][j]=0),也直接返回0;
    3.对于(p[i][j]=1)的点,我们要保证(i)(j)在一棵树里;对于(p[i][j]=2)的点,要保证(i)(j)在不同的树里。因此用并查集合并所有(p[i][j]=1)(i)(j),再判断(p[i][j]=2)的两点是否在一个并查集里,是的话就直接返回0;
    4.现在这个连通块一定存在合法的图:我们取每一个并查集的任意一个元素(以代表元为例),将他作为基环树的树根构成环,剩下的点直接以这个点为端点连成一条链即可(因为链也保证了两点间路径条数为1)。这期间还要判断在有(p[i][j]=2)的前提下参与构成环的点是否大于等于3,否则也要返回0.


    写起来还是不难的。

    //#include "supertrees.h"
    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define In inline
    typedef long long ll;
    const int maxn = 1e3 + 5;
    
    void build(std::vector<std::vector<int> > b);
    typedef vector<int> Ivec;
    
    vector<Ivec> E;
    int n, a[maxn], cnt = 0;
    bool vis[maxn];
    In void dfs(int now)
    {
    	vis[now] = 1, a[cnt++] = now;
    	for(int i = 0; i < n; ++i) if(!vis[i] && E[now][i]) dfs(i);
    }
    
    int p[maxn];
    In int Find(int x) {return x == p[x] ? x : p[x] = Find(p[x]);} 
    In void merge(int x, int y) 
    {
    	int px = Find(x), py = Find(y);
    	if(px ^ py) p[px] = py;
    }
    In bool check0()
    {
    	for(int i = 0; i < cnt; ++i)
    		for(int j = i + 1; j < cnt; ++j) if(!E[a[i]][a[j]]) return 0;
    	return 1;
    }
    bool FLG2 = 0;
    In bool check2()
    {
    	for(int i = 0; i < cnt; ++i)
    		for(int j = i + 1; j < cnt; ++j)
    			if(E[a[i]][a[j]] == 2)
    			{
    				FLG2 = 1;
    				if(Find(a[i]) == Find(a[j])) return 0;	
    			} 
    	return 1; 
    }
    In void divide()
    {
    	for(int i = 0; i < cnt; ++i)
    		for(int j = i + 1; j < cnt; ++j) if(E[a[i]][a[j]] == 1) merge(a[i], a[j]);
    }
    int c[maxn], las[maxn], ccnt = 0;
    In bool buildGraph(vector<Ivec>& G)
    {
    	ccnt = 0;												//get the circle
    	for(int i = 0; i < cnt; ++i) Find(a[i]);
    	for(int i = 0; i < cnt; ++i) if(p[a[i]] == a[i]) c[ccnt++] = las[a[i]] = a[i];
    	if(FLG2 && ccnt < 3) return 0;
    	if(ccnt > 2)
    	{
    		for(int i = 0; i < ccnt - 1; ++i) G[c[i]][c[i + 1]] = G[c[i + 1]][c[i]] = 1;
    		G[c[0]][c[ccnt - 1]] = G[c[ccnt - 1]][c[0]] = 1;
    	}
    	for(int i = 0, x = a[i]; i < cnt; ++i, x = a[i])		//build tree
    		if(x != p[x]) 
    		{
    			G[x][las[p[x]]] = G[las[p[x]]][x] = 1;
    			las[p[x]] = x;
    		}
    	return 1;
    }
    int construct(vector<Ivec> e)
    {
    	n = e.size();
    	for(int i = 0; i < n; ++i)
    		for(int j = 0; j < n; ++j) if(e[i][j] == 3) return 0;
    	E = e; vector<Ivec> G(n, Ivec(n));
    	for(int i = 0; i < n; ++i) p[i] = i;
    	for(int i = 0; i < n; ++i) if(!vis[i])
    	{
    		cnt = 0, dfs(i);
    		if(!check0()) return 0;
    		divide(), FLG2 = 0;
    		if(!check2()) return 0;
    		if(!buildGraph(G)) return 0; 
    	}
    	build(G);
    	return 1;
    }
    
  • 相关阅读:
    Uva673 Parentheses Balance
    cordforce Educational Codeforces Round 47 补题笔记 <未完>
    cordforce 495 补题 <未完>
    linux 基本命令笔记
    app审核相关
    CATransform3D
    UITableView点击切换状态分析
    iOS大转盘抽奖
    被忽视的控件UIToolbar
    AVPlayerViewController视频播放器
  • 原文地址:https://www.cnblogs.com/mrclr/p/14120268.html
Copyright © 2020-2023  润新知