首页 > 其他分享 >AT_apc001_g Colorful Doors 题解

AT_apc001_g Colorful Doors 题解

时间:2023-08-10 19:14:47浏览次数:35  
标签:ch 匹配 传送门 题解 消掉 Doors apc001 配对 define

模拟赛做到的题,场上写贪心爆栈了qwq


首先在首尾加上两个 \(1\) 表示进出,将两段路中间的间隔作为传送门,恰好有
\(2 \times N\) 个传送门,根据两段路的经过情况给传送门分类别:

00:用 \(N\) 表示,称为无用点,不到达该点。

10:用 \(S\) 表示,称为起点,需要通过向右走走到一次。

01:用 \(T\) 表示,称为终点,需要通过传送到达一次。

11:用 \(M\) 表示,称为中转点,需要分别通过向右和传送到达两次。

容易发现:中转点需要两两配对,起点和终点配对,无用点相互配对。

所以以下三种情况可以直接判无解:

  1. 中转点个数不为2的倍数
  2. 起点个数 \(\neq\) 终点个数
  3. 无用点个数不为2的倍数

接下来考虑具体的配对问题:

对于中转点 \(M\):考虑将连续的四个 \(M\) 以 1212 的配对方式消掉,如图所示:

事实上不连续的四个 \(M\) 也可以这样消掉,因为 \(M\) 的左右对应的路都是 \(1\),左相邻的传送门只能是 \(T/M\),右响铃的传送门只能是 \(S/M\),也就是说两段连续的 \(M\) 之间只可能是不断重复的 \(ST\) ,将 \(ST\) 匹配消掉即可,如图所示:

综上所述:当 \(M\) 的个数是 \(4\) 的倍数时,只需要将所有 \(M\) 从左往右按照 1212 的配对方式配对即可。


当 \(M\) 的个数不为 \(4\) 的倍数时,因为 \(M\) 需要两两匹配,有解时一定为 \(2\) 的倍数,匹配时考虑将两个 \(M\) 消掉,对剩下的 \(M\) 按上面的方法匹配。

设两个 \(M\) 中位置靠前的为 \(M_{1}\),位置靠后的为 \(M_{2}\),因为无法使用更多的 \(M\),我们需要通过 \(M_{1}/M_{2}\) 所处连续段的前后的 \(T\) 和 \(S\) 让当前所处位置倒退,以此来二次经过通过传送到达的 \(M_{2}\),这样我们就用一对 \(ST\) 消掉了两个 \(M\),中间的连续的 \(M\) 与其它段的 \(M\) 结合,通过之前的方法消掉,然后会二次经过 \(M_{2}\),正好将落单的 \(ST\) 消掉,两种情况分别如图所示:

如果两种情况均不满足,比如 MSTM 的情况,判成无解。

细节:因为此时的配对起始位置发生了改变,所以将剩下的 \(M\) 消掉时不能像之前一直接样从左往右,要先记录中间段的 \(M\),再对剩下未匹配的 \(M\) 从左往右记录进行配对。

对于剩下未匹配的 \(S\) 和 \(T\) 和 \(N\) ,直接找到右边第一个还未匹配的对应匹配即可。

时间复杂度:\(\mathcal{O}(n)\)


\(\mathcal{Code}\):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define db double
#define il inline
#define re register
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define F(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define DF(i,a,b) for(int (i)=(a);(i)>=(b);(i)--)
#define G(i,u) for(int (i)=head[u];(i);(i)=nxt[(i)])
inline ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}return x*f;}
const int N=200010;
int n,num,tot;
char str[N];
int a[N],t[N],c[N];//1:st 2:nd 3:st+nd
int M[N];
int main()
{
	n=read()<<1;
	scanf("%s",str);
	F(i,1,n-1) a[i]=str[i-1]-'0';
	a[0]=a[n]=1;
	int cntS=0,cntT=0,cntM=0;
	F(i,1,n)
	{
		if(a[i-1]==1&&a[i]==0) t[i]=1,cntS++;
		if(a[i-1]==0&&a[i]==1) t[i]=2,cntT++;
		if(a[i-1]==1&&a[i]==1) t[i]=3,cntM++;
	}
	if(cntS!=cntT||cntM%2)//判无解
	{
		printf("No");
		return 0;
	}
	int lstL=0,lstR=0,flag=0;
	if(cntM%4)
	{
		for(int l=1,r=1;l<=n;l=r)
		{
			while(t[r]==3&&r<=n) r++;
			r--;//方便记录区间
			if(lstL&&lstR)
			{
				if(t[l-1]==2&&t[r+1]==1)//右边有S+T 
				{
					c[lstL]=c[r]=++num;
					c[l-1]=c[r+1]=++num;
					flag=1;
				}
				else if(t[lstL-1]==2&&t[lstR+1]==1)//右边无S+T,左边有S+T 
				{
					c[l]=c[lstR]=++num;
					c[lstL-1]=c[lstR+1]=++num;
					flag=1;
				}
				F(i,l,r)//因为起始点变化了,要先将中间段推入数组 
					if(!c[i]&&t[i]==3)
						M[++tot]=i;
				lstL=l,lstR=r;
				break;
			}
			else lstL=l,lstR=r;
			r++;//归位
			while(t[r]!=3&&r<=n) r++;
		}
		if(!flag)//判无解
		{
			printf("No");
			return 0;
		}
	}
	F(i,1,lstL-1)//挖去中间段,对剩下块统计 
		if(!c[i]&&t[i]==3)
			M[++tot]=i;
	F(i,lstR+1,n)//挖去中间段,对剩下块统计
		if(!c[i]&&t[i]==3)
			M[++tot]=i;
	int lstS=0,lstT=0,lstN=0;
	F(i,1,n)//对剩下的进行统计
	{
		if(c[i]) continue;
		if(t[i]==1)
		{
			if(lstT) c[i]=c[lstT]=++num,lstT=0;
			else lstS=i;
		}
		else if(t[i]==2)
		{
			if(lstS) c[i]=c[lstS]=++num,lstS=0;
			else lstT=i;
		}
		else if(!t[i])
		{
			if(lstN) c[i]=c[lstN]=++num,lstN=0;
			else lstN=i;
		}
	}
	for(int i=1;i<=tot;i+=4)//对剩下的M进行匹配
	{
		c[M[i]]=c[M[i+2]]=++num;
		c[M[i+1]]=c[M[i+3]]=++num;
	}
	printf("Yes\n");
	F(i,1,n)
		printf("%d ",c[i]);
	return 0;
}

标签:ch,匹配,传送门,题解,消掉,Doors,apc001,配对,define
From: https://www.cnblogs.com/MooSheng/p/17621274.html

相关文章

  • 关于Promise的超难面试题解读
    让我来看一下题目,如下所示Promise.resolve().then(()=>{ console.log(0); returnPromise.resolve(4); }).then((res)=>{ console.log(res); }); Promise.resolve().then(()=>{ console.log(1); }).then(()=>{ console.log(2); }).t......
  • 题解 [SDOI2009] Elaxia的路线
    题目链接题意简述:求两条给定起点终点最短路的最长公共路径。首先最长公共路径一定是两条最短路的公共最长链的部分。至少一定在两条最短路上。考虑如何求出一条路径是否包含于一条最短路,只要路径\(x\rightarrowy\)满足:\[dis_{st\rightarrowx}+w_{x\rightarrowy}+dis_{y\r......
  • keepalived 邮件通知无法发送邮件问题解决【亲测有效,没有效果来找我】
    环境keepalivedkeepalived-2.2.7操作系统cenos7安装方式源码编译安装问题最近在安装keepalived高可用服务,环境是安装完了,但是我想要使用邮件通知这个功能,通过网上捞针怎么也不成功,真是绝绝子,折磨我1天多。终于在刚刚得到了解决办法解决在vrrp_instance自定义的名字中添加......
  • iPhone上使用Charles 抓包的配置方法与问题解决方式
    我是在Macos下配置的,其它平台的内容和步骤也差不多。配置方法:(网上很多,大致说下)一、Charles下载:1)官网下载地址:https://www.charlesproxy.com/download/  二、Charles配置代理:1)查看本机IP:help-->LocalIPAddress   2)查看或者设置访问端口:Proxy->ProxySettings三、配置ios手......
  • P9511 『STA - R3』大豆 题解--zhengjun
    妙妙题。题意给定\(F_0(x)=a_{(x-1)\bmodn+1}\)。\[F_k(x)=F_{k-1}(x)-\sum\limits_{i=2}^nF_k(\lfloor\frac{n}{i}\rfloor)\]求\(F_k(m)\)。\(1\len\le10^4,1\lem\le10^{10},1\lek\le3\)。直接数论分块求解的复杂度是\(O(m^{\frac{3}{4}}k)\),难以通过。如果像......
  • 大连人工智能计算平台——华为昇腾AI平台——高性能计算HPC的pytorch源码编译报错——
     如题:pytorch源码编译报错——USE_CUDA=OFF  在编译pytorch源码的时候发现错误,虽然编译环境中已经安装好CUDA和cudnn,环境变量也都设置好,但是编译好的pytorch包wheel总是在运行torch.cuda.is_available()显示false,于是从编译源码的过程中进行重新检查,发现在编译的过程中提......
  • 8 月 9 日测试题解
    集体被大样例薄纱了。T1P1292有两个容量分别为\(a\)与\(b\)的酒杯与一个容量无限的酒桶,有以下几种操作:用酒桶将\(a\)倒满;将\(b\)中的酒全部倒入酒桶;将\(b\)中的酒倒入\(a\),直到\(a\)被装满或\(b\)被倒空。问\(a\)中可能倒出的最少的酒是多少以及分别至......
  • 题解 CF1857G【Counting Graphs】
    一个非常显然的事情是:总方案数即为每条边方案数之积。树边已经确定,考察每条非树边\((u,v)\)可以怎么取。给定的树\(T\)是唯一最小生成树,这意味着非树边\((u,v)\)要么不存在,要么权值大于\(T\)上\((u,v)\)之间任意一条边的权值。设\(T\)上\((u,v)\)间的最大边权为\(......
  • 杭电多校 2023 杂题题解
    打算只写点有意思的题。D1JEasyproblemI注意到\(x_i\)单增,所以一个数被减到负数之后,所有的操作都会将它减到负数,也就等价于乘\(-1\)再相加。使用一棵线段树维护所有数,将这些数分为两种,一种如上,一种是区间减。最终所有数都会变为需要乘\(-1\)再相加的数,于是只要每次精......
  • luogu P4200 千山鸟飞绝 题解 【一维数组套平衡树】
    目录题目解题思路code题目题目链接解题思路首先,此题有明显的插入、删除、查找,所以必须要使用平衡树。考虑如何使用平衡树维护每个鸟的状态。发现很不方便,因为鸟的位置改变,整个平衡树的值都要修改。考虑针对每个节点开一颗平衡树,这样就有\(3e4\times3e4\)棵树。这显然太多了......