CF798E Mike and code of a permutation

一、题目

点此看题

二、解法

首先题目的限制显然可以转成若干偏序关系:

  • 如果 (a_i=-1),那么找到所有未被标记的 (jin[1,n]),把 (j)(i) 连一条边,表示 (p_j<p_i)
  • 如果 (a_i ot=-1),那么找到所有未被标记的 (jin[1,a_i)),把 (j)(i) 连一条边,表示 (p_j<p_i);然后把 (i)(a_i) 连一条边,表示 (p_i<p_{a_i})

考虑对这个图暴力拓扑,时间复杂度 (O(n^2)),考虑优化。一个 ( t naive) 的思路是把所有点的入度维护出来,但是发现并不好维护。

这时候要用一个常见转化:如果不是计数问题,那么可以考虑把维护数量转化成维护最值。

那么原来的拓扑做法肯定不能要了,这里给出一种崭新的拓扑方法:建出原图的反图,然后枚举 (1 ightarrow n),如果当前点没有被访问过就以其为起点在反图上 ( t dfs),最后回溯的顺序就是拓扑序。

那么这种方法就只需要知道反图上一个点连出去的点是什么,也就是权值大( ightarrow)权值小的边。设 (b_i) 表示 (i) 点被标记的时间点,如果没有被标记 (b_i=n+1);如果 (a_i=-1) 我们令 (a_i=n+1)。考虑只会有两种边:(i ightarrow a_i(a_i ot=n+1))(i ightarrow j(j<a_i,i<b_j))

我们不用真正维护出边,而是选出最有可能被遍历的那个点去更新它,依据这个思路把它转成最值问题。也就是我们对 (i) 为下标建立线段树,维护 (b_i) 的最大值,更新时找到 (b_j) 最大的 (jin[1,a_i)) 看满不满足 (b_j>i),显然如果不满足的话也就没有其它出边了,时间复杂度 (O(nlog n))

三、总结

普通算法的另类形式一定要注意积累,很多奇怪题会用到(最小生成树的另类形式是最热门的)

维护什么东西值得思考,注意维护数量到维护最值的转化。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 500005;
#define pii pair<int,int>
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,cnt,a[M],b[M],c[M];pii mx[4*M];
void ins(int i,int l,int r,int id)
{
	if(l==r)
	{
		mx[i]=make_pair(b[id],id);
		return ;
	}
	int mid=(l+r)>>1;
	if(mid>=id) ins(i<<1,l,mid,id);
	else ins(i<<1|1,mid+1,r,id);
	mx[i]=max(mx[i<<1],mx[i<<1|1]);
}
pii ask(int i,int l,int r,int L,int R)
{
	if(L>r || l>R) return make_pair(0,0);
	if(L<=l && r<=R) return mx[i];
	int mid=(l+r)>>1;
	return max(ask(i<<1,l,mid,L,R),
	ask(i<<1|1,mid+1,r,L,R));
}
void dfs(int u)
{
	int k=b[u];b[u]=0;
	ins(1,1,n,u);
	if(k!=n+1 && b[k]) dfs(k);
	while(1)
	{
		pii r=ask(1,1,n,1,a[u]-1);
		if(r.first<=u) break;
		dfs(r.second);
	}
	c[u]=++cnt;
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		if(a[i]!=-1) b[a[i]]=i;
	}
	for(int i=1;i<=n;i++)
	{
		if(a[i]==-1) a[i]=n+1;
		if(!b[i]) b[i]=n+1;
		ins(1,1,n,i);
	}
	for(int i=1;i<=n;i++)
		if(!c[i]) dfs(i);
	for(int i=1;i<=n;i++)
		printf("%d ",c[i]);
}
原文地址:https://www.cnblogs.com/C202044zxy/p/15194017.html