题解 | Guessing ETT-2019牛客暑期多校训练营第三场C题

题目来源于牛客竞赛:https://ac.nowcoder.com/acm/contest/discuss
题目描述:
ZYB is a smart guy.
One day he learns a new method for representing trees: Euler tour technique (ETT). 、
You can find more details about ETT on this web page:

https://en.wikipedia.org/wiki/Euler_tour_technique
https://blog.nowcoder.net/n/7afe88dc36ef4649b4de941f08abf576

If we use vertices rather than edges in ETT, then any tree with N ertices corresponds to a sequence of length 2N−1, let’s call it the vertex-ETT sequence.

In the beginning, ZYB generates a tree (the root of that tree is always 1) and writes down its vertex-ETT sequence.
However, he spilt ink onto the paper by mistake and some numbers were covered in ink.
Can you help him to restore the sequence?

输入描述:
There are multiple test cases. The first line of the input contains an integer T, indicating the number of test cases.
For each test case, the first line contains a integer N (1≤N≤2.5×105),
while the second line contains an integer sequence a(1≤aiN or ai=-1,which means this number was covered by the ink) of length 2N-1, the vertex-ETT sequence.

it is guaranteed that at least one valid sequence exists.
It’s guaranteed that the sum of N of all test cases will not exceed 500000.
Due to the large size of the input, it’s recommended to use a fast way to read the input.

输出描述:
For each case, print 2N-1space-separated integers, the recovered sequence.
If there are multiple solutions, print any of them.

示例1
输入
2
3
1 2 1 -1 1
3
-1 2 3 -1 1

输出
1 2 1 3 1
1 2 3 2 1

题解:
• 一个想当然的做法是,实时维护一个栈,表示目前所到的点到根路径的所有点。
• 一开始先把k放进去。
• 如果遇到一个非-1的数,很简单:
• 如果在栈的倒数第二个位置,就弹出栈的最后一个数字。
• 否则,将这个数加入栈。
• 遇到一个-1,看起来是优先弹栈,如果不能弹了,就用一个没用过的数字?

• 看一个简单的例子:1 ?3 ??4 ?2 1
• 怎么知道第二格要填1呢?
• 如果不填对不对?好像可以!1 5 3 5 1 4 1 2 1!
• 但是如果是这个例子:1 ?3 4 ?2 ??1
• 就不能直接写5了,只能填4或者2。
• 有些时候需要强制填一个后面出现过的数字。
• 假设当前位置是i,后面那个数的位置是j,那么有判定条件:
• (Sum[j-1]-Sum[i])*2=j-i

• 注意还有一个条件是:I 和 j 的奇偶性要相同。
• 上述式子可以简单地线性维护。
• 这样 -1 的流程也就出来了:
• 如果存在一个>i的j满足上述式子,挑一个最小的j,填上对应数字。
• 否则,看能否退栈,能就退,不能就进。
• 这题细节还是比较繁琐的,建议都去实现以下。
• 如果实现精细,复杂度是 O(N) 的。

代码:

#include<bits/stdc++.h>
#define N 1000005
using namespace std;
int a[N],b[N],num[N],n,tot,Case;
vector<int>e[N],app[N],can;

int ch[N],sum[N];
set<pair<int,int> >S[2];
void Color(vector<int>be,int around){
	static int sum[N];
	static vector<int>st;st.clear();st.push_back(around);
	sum[0]=be[0];S[0].clear();S[1].clear();
	for (int i=1;i<be.size();i++){
		sum[i]=sum[i-1]+(be[i]>0);
		if (be[i]>0) S[i&1].insert(make_pair(i-2*sum[i-1],i));
	}
	++Case;
	for (int i=0;i<be.size();i++)
		if (be[i]>0){
			if (st.size()>1&&st[st.size()-2]==be[i])
				st.pop_back();
			else st.push_back(be[i]);
		}
		else {
			//j>i  (Sum_{j-1}-Sum_i)*2=j-i  i-2*Sum[i] = j-2*Sum[j-1]
			int j=be.size();
			while (true){
				auto it=S[i&1].lower_bound(make_pair(i-2*sum[i],0));
				if (it==S[i&1].end()||(*it).first!=i-2*sum[i]) break;
				if ((*it).second<=i) S[i&1].erase(it);
				else {j=(*it).second;break;}
			}
			if (j<be.size()){
				a[-be[i]]=be[j];
				if (ch[be[j]]==Case){
					while (st.back()!=be[j]) st.pop_back();
				}
				else ch[be[j]]=Case,st.push_back(be[j]);
			}
			else {
				if (st.size()>1){
					a[-be[i]]=st[st.size()-2];
					st.pop_back();
				}
				else{
					assert(can.size());
					st.push_back(a[-be[i]]=can.back());
					can.pop_back();
				}
			}
		}
}
void solve(int l,int r,int around){
	if (l>r) return;
	vector<int>be;
	for (int i=l;i<=r;i++)
		if (a[i]==-1) be.push_back(-i);
		else {
			for (int k=0;k+1<app[a[i]].size();k++)
				solve(app[a[i]][k]+1,app[a[i]][k+1]-1,a[i]);
			be.push_back(a[i]);
			i=app[a[i]].back();
		}
	Color(be,around);
}
void euler(int x){
	b[++tot]=x;
	for (int i=0;i<e[x].size();i++)
		euler(e[x][i]),b[++tot]=x;
}
void read(int &x){
	char ch=getchar();x=0;int f=1;
	for (;ch<'0';ch=getchar()) if (ch=='-') f=-1;
	for (;ch>='0';ch=getchar()) x=x*10+ch-'0';x*=f;
}
void Print(int x){
	static int Q[20];*Q=0;
	for (;x;x/=10) Q[++*Q]=x%10;
	for (int i=*Q;i;--i) putchar('0'+Q[i]);
}
int main(){
	int sumN=0;
	int T;read(T);
	while (T--){
		read(n);sumN+=n;
		for (int i=1;i<2*n;i++) read(a[i]);
		a[1]=a[2*n-1]=1;
		for (int i=1;i<=n;i++) app[i].clear();can.clear();
		for (int i=1;i<2*n;i++)
			if (a[i]!=-1) app[a[i]].push_back(i);
		for (int i=1;i<=n;i++)
			if (!app[i].size()) can.push_back(i);
		for (int k=0;k+1<app[1].size();k++)
			solve(app[1][k]+1,app[1][k+1]-1,1);
		for (int i=1;i<2*n;i++)
			Print(a[i]),putchar(i==2*n-1?'\n':' ');
	}
	//fprintf(stderr,"%d\n",sumN);
}

更多问题,更详细题解可关注牛客竞赛区,一个刷题、比赛、分享的社区。
传送门:https://ac.nowcoder.com/acm/contest/discuss

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值