zoukankan      html  css  js  c++  java
  • 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]);
    }
    
  • 相关阅读:
    XML HTML CSS JS的区别整理
    Python的作用域
    python文件操作总结
    python的open文件操作
    python使用struct处理二进制
    webstorm
    vue-cli项目中,配合element_ui来实现上传图片与视频到oss上。
    vue-cli项目中如何实现局部页面的刷新?
    基于cropper图片裁剪,兼容PC端与移动端
    跨域的完全讲解
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15194017.html
Copyright © 2011-2022 走看看