传送门

毒瘤细节题。

首先考虑不合法的情况。

先把相同的值配对,这样就构成了一些区间。

那么如果这些区间有相交的话,就不合法了。

如何判断?DZYO安利了一波st表,我觉得很不错。

接着考虑两个相同的值,它们中间一定只有奇数个数。

然后剩下不合法的情况可以在接下来处理时判断。

接下来还原序列的问题是可以拆分成子问题的。

考虑这两个相同的值夹住的区间。

显然这个区间里是没有值相同的。

对于区间里两个相邻且不全为0的数。

如果是形如0xy0xy0xy的话,我们把0改成y可以变成一个可行解yxyyxyyxy。

而如果是形如xy0xy0xy0的话,我们把0改成x可以变成一个可行解xyxxyxxyx。

如果连续三个都不同那一定是凉了。

这样的话,对于头两种情况,我们用一个并查集维护一种操作。

如果是第一种情况,我们可以直接丢掉0x0x0x只保留y在序列中同时把答案数组中0对应的值赋为y。

第二种同理。

这样操作完整个序列之后序列会严重缩水变成:

X0...0A10...0A20...0A30...0XX0...0A_10...0A_20...0A_30...0XX0...0A1​0...0A2​0...0A3​0...0X

然后继续操作。

如果两端都为0或者都为相同的数直接递归子问题。

如果了两端有一个不为0的,把为0的变成跟它相同的就行了。

最后有点细节。

如果这一次处理的区间为[L,R][L,R][L,R],我们再用一个并查集把它们并在一起,下一次直接当成一个点就行了。

p.s.由于本蒟蒻很菜,代码是照着DZYO学长写的。

代码:

#include<bits/stdc++.h>
#define N 1000005
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int n,m,in[N],pred[N],last[N],cnt[N],len,st[N][21],L[N],R[N];
vector<int>del;
inline int min(int a,int b){return a<b?a:b;}
inline int find(int*fa,int x){return x==fa[x]?x:fa[x]=find(fa,fa[x]);}
inline bool solve(int l,int r){
	vector<int>pos;
	int Ri=-1;
	for(int i=find(R,l);i<=r;i=find(R,i+1)){
		pos.push_back(i),++Ri;
		while(pos.size()>=3){
			if(((!in[pos[Ri-2]]&&in[i])||(in[pos[Ri-2]]&&!in[i])||(in[pos[Ri-2]]&&(in[pos[Ri-2]]==in[i])))&&in[pos[Ri-1]]){
				if(in[pos[Ri-2]]!=in[pos[Ri]])in[pos[Ri-2]]=in[i]=in[pos[Ri-2]]+in[i];
				pos.pop_back(),pos.pop_back(),Ri-=2;
			}
			else break;
		}
	}
	for(int i=1;i<pos.size();++i)if(in[pos[i]]&&in[pos[i-1]]){puts("no");return 0;}
	deque<int>q;
	int Le=pos[0];
	for(int i=1;i<pos.size()-1;++i)q.push_back(pos[i]);
	while(!q.empty()){
		int x=q.front(),y=q.back();
		if(q.size()==1){
			if(!in[x]){
				if(del.empty()){puts("no");return 0;}
				in[x]=del.back(),del.pop_back();
			}
			break;
		}
		else{
			if(!in[x]&&!in[y]){
				if(del.empty()){puts("no");return 0;}
				in[x]=in[y]=del.back(),del.pop_back(),Le=in[x],q.pop_front(),q.pop_back();
			}
			else if(in[x])in[q[1]]=Le,q.pop_front(),q.pop_front();
			else in[q[q.size()-2]]=Le,q.pop_back(),q.pop_back();
		}
	}
	for(int i=find(R,l);i<=r;i=find(R,i+1))if(i!=l)R[i]=r+1;
	L[r]=l;
	return 1;
}
int main(){
	n=read();
	for(int i=1;i<n*2;++i)in[i]=read(),++cnt[in[i]];
	for(int i=1;i<=n;++i)if(!cnt[i])del.push_back(i);
	if(in[1]&&in[n*2-1]&&in[1]!=in[n*2-1]){puts("no");return 0;}
	else if(!in[1]&&!in[n*2-1]){
		if(del.empty()){puts("no");return 0;}
		in[1]=in[n*2-1]=del.back(),del.pop_back();
	}
	else if(in[1]!=in[n*2-1])in[1]=in[n*2-1]=in[1]+in[n*2-1];
	for(int i=1;i<n*2;++i){
		if(!in[i])continue;
		pred[i]=last[in[i]]?last[in[i]]:i,last[in[i]]=i;
	}
	for(int i=1;i<n*2;++i){
		st[i][0]=pred[i]?pred[i]:i;
		if(in[i]&&((pred[i]&1)!=(i&1))){puts("no");return 0;}
		for(int j=1;j<=20&&(1<<j)<=i;++j)st[i][j]=min(st[i][j-1],st[i-(1<<(j-1))][j-1]);
		if(in[i]&&pred[i]<i){
			int mn=i,pre=i-1;
			for(int j=20;~j;--j)if(pre-(1<<j)+1>pred[i])mn=min(mn,st[pre][j]),pre-=(1<<j);
			if(mn<=pred[i]){puts("no");return 0;}
		}
	}
	for(int i=1;i<=n*2;++i)L[i]=R[i]=i;
	for(int i=2;i<n*2;++i){
		if(!in[i]||find(L,pred[i])>=i)continue;
		if(!solve(find(L,pred[i]),i))return 0;
	}
	puts("yes");
	for(int i=1;i<n*2;++i)printf("%d ",in[i]);
	return 0;
}

2018.09.25 codeforces1053E. Euler tour(并查集+st表+模拟)的更多相关文章

  1. 2018.09.12 hdu2473Junk-Mail Filter(并查集)

    传送门 一开始开题还以为是平衡树. 仔细想了一想并查集就可以了. 合并操作没什么好说的. 删除操作:对于每个点记录一个pos值表示原来的点i现在的下标是什么. 每次删除点i是就新建一个点cnt,然后令 ...

  2. 洛谷P3295 萌萌哒 并查集 + ST表

    又切一道紫题!!! 成功的(看了一吨题解之后),我A掉了第二道紫题. 好,我们仔细观察,发现这是一个排列组合问题. 有些限定条件,要相等的地方,我们就用并查集并起来.最后一查有多少个并查集,就有多少个 ...

  3. bzoj 4569 [Scoi2016]萌萌哒 并查集 + ST表

    题目链接 Description 一个长度为\(n\)的大数,用\(S_1S_2S_3...S_n\)表示,其中\(S_i\)表示数的第\(i\)位,\(S_1\)是数的最高位,告诉你一些限制条件,每 ...

  4. luogu3295 萌萌哒 (并查集+ST表)

    如果给相同的位置连边,最后联通块数是n,最后答案就是$9*10^{n-1}$ 但直接连边是$O(n^2)$的 所以事先处理出一个ST表,每次O(1)地给那个ST表连边 最后再一点一点下放,就是把在这层 ...

  5. 【并查集】【模拟】Codeforces 698B & 699D Fix a Tree

    题目链接: http://codeforces.com/problemset/problem/698/B http://codeforces.com/problemset/problem/699/D ...

  6. 2018.08.21 bzoj4668: 冷战(并查集+启发式合并)

    传送门 可以发现需要维护连通性和两点连通时间. 前者显然是并查集的常规操作,关键就在于如何维护两点的连通时间. 然后会想到这个时候不能用路径压缩了,因为它会破坏原本树形集合的结构,因此可以启发式按si ...

  7. 【11.1校内测试】【快速幂DP】【带权并查集】【模拟】

    Solution $jzy$大佬用了给的原根的信息,加上矩阵快速幂150行QAQ 然而$yuli$大佬的做法不仅好懂,代码只有50行! 快速幂的思想,把m看成要组成的区间总长度,每次将两段组合得到新的 ...

  8. 【2-SAT】【并查集】NOIp模拟题 植树方案 题解

        一个类似2-SAT的思想,但是简化了很多.只需要用到并查集实现. 题目描述 企鹅国打算种一批树.所谓树,就是由$N$个结点与$N-1$条边连接而成的连通无向图.企鹅国的国王对于这些树有下列要求 ...

  9. 2018.09.25 poj2068 Nim(博弈论+dp)

    传送门 题意简述:m个石子,有两个队每队n个人循环取,每个人每次取石子有数量限制,取最后一块的输,问先手能否获胜. 博弈论+dp. 我们令f[i][j]f[i][j]f[i][j]表示当前第i个人取石 ...

随机推荐

  1. JavaScript加法

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <hea ...

  2. libcur+openssl的编译,使之支持SSL<转>

    本机环境: Visual Studio 2010 . Windows 7 64 bit 1: 下载文件 1.1 libcurl: curl-7.49.1.zip 地址: https://curl.ha ...

  3. Simple2D-21(重构)渲染部分

    以前 Simple2D 的渲染方法是先设置 Pass,然后添加顶点数据,相同 Pass 的顶点数据会合并在一起.当设置新的 Pass 时,将旧的 Pass 和对应的顶点数据添加到渲染数组中.最后在帧结 ...

  4. ABAP-动态编程

    转载:http://www.cnblogs.com/jiangzhengjun/p/4293407.html 动态编程 动态的基本语法 多种不同的动态编程 动态字段 动态类型 指定结构.内表组件字段的 ...

  5. Asp.net MVC重要

    1.asp.net mvc百度解释 2.asp.net mvc各版本特点 3.asp.net mvc知多少 4.asp.net mvc4入门到精通系列目录汇总(邹琼俊)[重要] 5.新年奉献MVC+E ...

  6. JDK5并发(1) Locks-AQS

    AbstractQueuedSynchronizer @(Base)[JDK, locks, ReentrantLock, AbstractQueuedSynchronizer, AQS] 转载请写明 ...

  7. 如何将Wav文件做到EXE文件里

    1)编写.RC文件 ..RC文件是资源的源文件,编译器也就编译这个文件,生成.RES的资源文件 首先在我们的项目子目录中建立一个纯文本文件,起名叫Sound.rc,文件中 有一行,内容为: SOUND ...

  8. 如何勾选 servlet如何获取?

    1.jsp中checkbox <form action="Test"> <% for(int i = 0 ; i < 10 ; i++){ %> &l ...

  9. javascript中的二维数组

    要创建一个二位数组我们脑子里第一个出现的就是 var arr=[][]; 但是在javascript这样是会报错的,要在javascrip中创建一个二位数组对象方法如下 方法一     直接把数组写出 ...

  10. 新手C#SQLServer在程序里实现语句的学习2018.08.12

    从C#中连接到SQL Server数据库,再通过C#编程实现SQL数据库的增删改查. ado.net提供了丰富的数据库操作,这些操作可以分为三个步骤: 第一,使用SqlConnection对象连接数据 ...