首页 > 其他分享 >[IOI2023] 最长路程

[IOI2023] 最长路程

时间:2024-02-03 17:13:38浏览次数:22  
标签:le 路程 int back IOI2023 lt 地标 push 最长

题目描述

IOI 2023 组委会有大麻烦了!他们忘记计划即将到来的 Ópusztaszer 之旅了。然而,或许一切尚未为晚 ......

在 Ópusztaszer 有 \(N\) 个地标,编号为从 \(0\) 到 \(N-1\)。某些地标之间连有双向道路。任意一对地标之间至多连有一条道路。组委会不知道哪些地标之间有道路相连。

如果对于每三个不同的地标,它们之间都至少连有 \(\delta\) 条道路,我们就称 Ópusztaszer 的路网密度至少为 \(\delta\) 的。换言之,对所有满足 \(0 \le u \lt v \lt w \lt N\) 的地标三元组 \((u, v, w)\),配对 \((u,v)\),\((v,w)\) 和 \((u,w)\) 中至少有 \(\delta\) 个配对中的地标有道路相连。

组委会已知有某个正整数 \(D\),满足路网密度至少为 \(D\)。注意, \(D\) 的值不会大于 \(3\)。

组委会可以询问 Ópusztaszer 的电话接线员,以获取关于某些地标之间的道路连接信息。在每次询问时,必须给出两个非空的地标数组 \([A[0], \ldots, A[P-1]]\) 和 \([B[0], \ldots, B[R-1]]\)。地标之间必须是两两不同的,即,

  • 对于满足 \(0 \le i \lt j \lt P\) 的所有 \(i\) 和 \(j\),有 \(A[i] \neq A[j]\);
  • 对于满足 \(0 \le i \lt j \lt R\) 的所有 \(i\) 和 \(j\),有 \(B[i] \neq B[j]\);
  • 对于满足 \(0 \le i \lt P\) 且 \(0\le j \lt R\) 的所有 \(i\) 和 \(j\),有 \(A[i] \neq B[j]\)。

对每次询问,接线员都会报告是否存在 \(A\) 中的某个地标和 \(B\) 中的某个地标有道路相连。更准确地说,接线员会对满足 \(0 \le i \lt P\) 和 \(0\le j \lt R\) 的所有配对 \(i\) 和 \(j\) 进行尝试。如果其中某对地标 \(A[i]\) 与 \(B[j]\) 之间连有道路,接线员将报告 true。否则,接线员将报告 false

一条长度为 \(l\) 的路程,被定义为由不同地标 \(t[0], t[1], \ldots, t[l-1]\) 构成的序列,其中对从 \(0\) 到 \(l-2\)(包括 \(0\) 和 \(l-2\))的所有 \(i\),地标 \(t[i]\) 和 \(t[i+1]\) 之间都有道路相连。如果不存在长度至少为 \(l+1\) 的路程,则长度为 \(l\) 的某条路程被称为是最长路程

你的任务是通过询问接线员,帮助组委会在 Ópusztaszer 找一条最长路程。

【数据范围】

  • \(3 \le N \le 256\)
  • 对于每个测试用例,函数 longest_trip 的所有调用中 \(N\) 的累计总和不超过 \(1\,024\)。
  • \(1 \le D \le 3\)

询问次数不超过 400。

首先,连通块数量一定不超过 3.

如果连通块数量为2,那么两个连通块一定都是完全图。答案就是两个图中较大的那一个。

不然的话,现在要求一个连通图的最长路。

下面证明这个连通图一定存在哈密顿通路。

考虑现在对前 \(i\) 个数维护了一条哈密顿通路 \(u_1\rightarrow u_2\cdots u_k\),现在加入 \(i+1\)

  • 如果 \(i+1\) 与 \(u\) 有连边,连接 \(i+1\) 和 \(u\).
  • 如果 \(i+1\) 与 \(v\) 有连边,连接 \(i+1\) 和 \(v\).
  • 否则,\(u,v\) 有连边,原图是一个环。在当中二分出一个点 \(k\) 与 \(i+1\) 有连边,并把环断出来形成一条新的链。

同时我们得到了一个 询问次数 \(O(n\log n)\) 的做法。

一个神奇的优化:维护两条链,设两条链链头分别为 \(x\) 和 \(y\)。链底分别为 \(p\) 和 \(q\)。

考虑加入点 \(i\)

  • 如果 \(i\) 与 \(x\) 或 \(y\) 有连边,把 \(i\) 加入那条链
  • 否则 \(x,y\) 有连边,把两条链合并成一条,\(i\) 独自一条链。

最后把两条链合并起来。

  • 如果\(\{x,p\}\) 和 \(\{y,q\}\)之间有连边,直接接起来。
  • 否则两条链都是环,在两条链中分别二分出两个点,满足他们之间有边,并断环合并。

得到了一个 \(2n+2\log n\) 次询问的做法。

考虑优化, 400 大概是 \(1.5n+2\log n\),所以考虑用三次询问增加两个点。假设两个链链头分别为 \(x,y\),现在增加两个点 \(a,b\)

  • 如果 \(a,b\) 之间有边
    • 如果 \(x,y\) 之间有边,就把 \(x,y\) 连接,另一条链设为 \({a,b}\)
    • 否则,\(a\) 和 \(x,y\) 之间至少一个有边。用一次操作判断和哪个连接
  • 否则如果 \(x\) 和 \(a\) 有边,把 \(a\) 放到 \(x\) 后面。
    • 如果 \(a\) 和 \(y\) 有边,合并两条链,把 \(b\) 设为一条链
    • 否则 \(b\) 和 \(y\) 一定有边。
  • 否则 \(x\) 和 \(b\) 一定有边,\(y\) 的讨论同上。
//#include "longesttrip.h"
#include<bits/stdc++.h>
using namespace std;
vector<int>longest_trip(int N,int D);
bool are_connected(vector<int>A,vector<int>B);
vector<int>mk(vector<int>g)
{
	if(g.front()==g.back())
		return {g.front()};
	return {g.front(),g.back()};
}
std::vector<int> longest_trip(int N, int D)
{
	--N;
	if(!N)
		return {0};
	vector<int>g,h;
	g.push_back(1),h.push_back(0);
	for(int i=2;i<N;i+=2)
	{
		if(are_connected({i},{i+1}))
		{
			if(are_connected({g.back()},{h.back()}))
			{
				while(!h.empty())
					g.push_back(h.back()),h.pop_back();
				h.push_back(i),h.push_back(i+1);
			}
			else
			{
				if(are_connected({g.back()},{i}))
					g.push_back(i),g.push_back(i+1);
				else
					h.push_back(i),h.push_back(i+1);
			}
		}
		else
		{
			if(are_connected({g.back()},{i}))
			{
				g.push_back(i);
				if(are_connected({h.back()},{i}))
				{
					while(!h.empty())
						g.push_back(h.back()),h.pop_back();
					h.push_back(i+1);
				}
				else
					h.push_back(i+1);
			}
			else
			{
				g.push_back(i+1);
				if(are_connected({h.back()},{i+1}))
				{
					while(!h.empty())
						g.push_back(h.back()),h.pop_back();
					h.push_back(i);
				}
				else
					h.push_back(i);
			}
		}
	}
	if(N&1^1)
	{
		if(are_connected({g.back()},{h.back()}))
		{
			while(!h.empty())
				g.push_back(h.back()),h.pop_back();
			h.push_back(N);	
		}
		else
		{
			if(are_connected({g.back()},{N}))
				g.push_back(N);
			else
				h.push_back(N);
		}
	}
	if(are_connected(g,h))
	{
		if(are_connected(mk(g),mk(h)))
		{
			vector<int>p;
			if(are_connected({g.front()},{h.front()}))
			{
				p=g;
				reverse(p.begin(),p.end());
				for(int j:h)
					p.push_back(j);
			}
			else if(are_connected({g.front()},{h.back()}))
			{
				p=h;
				for(int j:g)
					p.push_back(j);
			}
			else if(are_connected({g.back()},{h.back()}))
			{
				p=g;
				for(int i=h.size()-1;~i;--i)
					p.push_back(h[i]);
			}
			else
			{
				p=g;
				for(int j:h)
					p.push_back(j);
			}
			return p;
		}
		int l=0,r=g.size()-1;
		while(l<r)
		{
			int md=l+r>>1;
			vector<int>p;
			for(int i=0;i<=md;i++)
				p.push_back(g[i]);
			if(are_connected(p,h))
				r=md;
			else
				l=md+1;
		}
		int k=l;
		l=0,r=h.size()-1;
		while(l<r)
		{
			int md=l+r>>1;
			vector<int>p;
			for(int i=0;i<=md;i++)
				p.push_back(h[i]);
			if(are_connected({g[k]},{p}))
				r=md;
			else
				l=md+1;
		}
		if(h.front()^h.back())
			assert(are_connected({h[l]},{g[k]}));	
		vector<int>p;
		for(int i=l-1;~i;--i)
			p.push_back(h[i]);
		for(int i=h.size()-1;i>=l;i--)
			p.push_back(h[i]);
		for(int i=k;~i;--i)
			p.push_back(g[i]);
		for(int i=g.size()-1;i>k;i--)
			p.push_back(g[i]);
		return p;
	}
	else
		return g.size()>h.size()? g:h;
}

标签:le,路程,int,back,IOI2023,lt,地标,push,最长
From: https://www.cnblogs.com/mekoszc/p/18004946

相关文章

  • 最长上升子序列总结
    这是最长上升子序列最基础的例子:给定一串数字32451那么他的最长上升子序列就是345其衍生问题为:求最长递减子序列、求正方向反方向最长递增/递减子序列求先上升后下降的最长子序列、求能完全覆盖整个序列的最小下降子序列个数求能完全覆盖整个序列的最小上升和下降......
  • 一次搭建GIT服务的漫长路程
    1、由于种种原因。需要在一台WindowSever2016的服务器上搭建Git服务。通过种种选择,发现GitLab是最适合的。但是GitLab只能在Linux上运行。而我能用的服务器的操作系统是WindowServer。所以只能在Window上通过docker容器或者虚拟机来运行GitLab服务了。 2、一开始我想到的是......
  • LOJ3990/LG9602 IOI2023 足球场 题解 (区间DP+精简无用状态)
    首先考虑一个足球场长啥样才是合法的。发现一个点能只拐弯一次到达另一个点,可以分为两种情况:先左右走,再上下走或先上下走,后左右走。无论哪种情况,都要求我们走一步使得和目标点一个轴相同,再走一步使得另一个轴也相同,所以加入把每一行选择的格子看成一个区间(因为如果不连续显然是......
  • 5.最长回文子串
    1.题目介绍给你一个字符串s,找到s中最长的回文子串。如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。示例1:输入:s="babad"输出:"bab"解释:"aba"同样是符合题意的答案示例2:输入:s="cbbd"输出:"bb"2.题解2.1动态规划思路对于一个子串而言,如果它是回......
  • Vue3 Diff算法之最长递增子序列,学不会来砍我!
    专栏分享:vue2源码专栏,vue3源码专栏,vuerouter源码专栏,玩具项目专栏,硬核......
  • NC91 最长上升子序列(三)
    https://www.nowcoder.com/practice/9cf027bf54714ad889d4f30ff0ae5481?tpId=117&rp=1&ru=%2Fexam%2Foj&qru=%2Fexam%2Foj&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3D%25E7%25AE%2597%25E6%25B3%2595%25E7%25AF%2587%26topicId%3D117&difficulty=&j......
  • 无重复字符的最长子串
    给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。示例 1:输入:s="abcabcbb"输出:3解释:因为无重复字符的最长子串是"abc",所以其长度为3。示例2:输入:s="bbbbb"输出:1解释:因为无重复字符的最长子串是"b",所以其长度为1。示例3:......
  • 无重复字符的最长子串2
    classSolution{public:intlengthOfLongestSubstring(strings){//哈希集合,记录每个字符是否出现过unordered_set<char>occ;intn=s.size();//右指针,初始值为-1,相当于我们在字符串的左边界的左侧,还没有开始移动int......
  • python 最长有效括号 多种解法
    使用栈:遍历字符串,当遇到左括号时,将其下标压入栈中;当遇到右括号时,如果栈为空,则将当前右括号下标作为新的起始点,否则将栈顶元素出栈,并计算当前有效括号的长度。Python代码示例:deflongest_valid_parentheses(s):stack=[-1]#栈中始终保持一个起始位置max_length=0......
  • #yyds干货盘点# LeetCode程序员面试金典:至少有 K 个重复字符的最长子串
    题目给你一个字符串s和一个整数k,请你找出s中的最长子串,要求该子串中的每一字符出现次数都不少于k。返回这一子串的长度。如果不存在这样的子字符串,则返回0。 示例1:输入:s="aaabb",k=3输出:3解释:最长子串为"aaa",其中'a'重复了3次。示例2:输入:s="aba......