• Atcoder Grand Contest 001 F


    Atcoder 题面传送门 & 洛谷题面传送门

    咦?鸽子 tzc 来补题解了?奇迹奇迹(

    首先考虑什么样的排列可以得到。我们考虑 (p) 的逆排列 (q),那么每次操作的过程从逆排列的角度思考,就可视作每次在逆排列中交换两个相邻,且元素值之差 (ge k) 的元素。注意到对于两个元素 (x,y),如果 (|x-y|<k),那么我们肯定永远无法交换它们,它们的相对位置顺序也永远无法改变,因为要改变它们的相对顺序必须交换它们。而一对 (|q_i-q_j|<k,i<j)((i,j)) 恰好对应一对 (p_i<p_j,|i-j|<k)((i,j))。因此对于相距 (<k) 的位置,它们的相对大小顺序不能发生变化。而我们能够证明,如果两个排列 (p,p'),满足它们相距 (<k) 的位置上的元素相对位置大小不变,那么它们之间就能够互相转化,证明我也不太会,一个感性地想法是,我们记排列 (q) 满足 (pcirc q=p'),那么我们每次总能交换两个数,使得 (q) 逆序对大小减一,因此它们能够互相转化。

    我们考虑对于 (|i-j|<k),如果 (p_i<p_j) 则连一条 (i o j) 的边,否则连一条 (j o i) 的边,那么不考虑复杂度的问题,我们的目标就是给每个点赋上一个标号 (r_i),使得对于所有有向边 (u o v),都有 (r_u<r_v),并且 (r) 的字典序尽可能小。这是一个经典问题,考虑这样的过程,我们建反图然后拓扑排序,每次取出编号最大的点然后在上面填 (n,n-1,cdots) 以此类推,类似的题有这个(它是道 Ag 的题但代码异常短)。但是暴力建图复杂度是 (nk) 的,无法通过。不过注意到此题建图方式很特殊,(n^2) 建边+特殊建图方式 (=nlog n)(暴论),注意到一个点 (x) 度为 (0),当且仅当 (p_x)([x-k+1,x+k-1]) 的最大值,因此我们考虑用线段树维护这个过程,先一遍扫过去将所有度为 (0) 的点压入优先队列,然后每次取出一个点 (x) 时,就将 (p_x) 改为 (-infty),然后松弛与其相连的点即可。不难发现每次松弛只有 ([x-k+1,x-1]) 中最大值的位置和 ([x+1,x+k-1]) 最大值的位置是有用的,只用松弛这两个点即可。

    时间复杂度 (nlog n)

    const int MAXN=5e5;
    int n,m,p[MAXN+5],res[MAXN+5];
    struct node{int l,r;pii mx;} s[MAXN*4+5];
    void pushup(int k){s[k].mx=max(s[k<<1].mx,s[k<<1|1].mx);}
    void build(int k,int l,int r){
    	s[k].l=l;s[k].r=r;if(l==r) return s[k].mx=mp(p[l],l),void();
    	int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    	pushup(k); 
    }
    void modify(int k,int p,pii v){
    	if(s[k].l==s[k].r) return s[k].mx=v,void();
    	int mid=s[k].l+s[k].r>>1;
    	(p<=mid)?modify(k<<1,p,v):modify(k<<1|1,p,v);
    	pushup(k);
    }
    pii query(int k,int l,int r){
    	if(l<=s[k].l&&s[k].r<=r) return s[k].mx;
    	int mid=s[k].l+s[k].r>>1;
    	if(r<=mid) return query(k<<1,l,r);
    	else if(l>mid) return query(k<<1|1,l,r);
    	else return max(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
    }
    priority_queue<int> q;
    bool inq[MAXN+5];
    void check(int x){
    	if(!x||inq[x]) return;
    	if(x==query(1,max(x-m,1),min(x+m,n)).se)
    		q.push(x),inq[x]=1;
    }
    int main(){
    	scanf("%d%d",&n,&m);--m;
    	for(int i=1;i<=n;i++) scanf("%d",&p[i]);
    	build(1,1,n);int c=n;
    	for(int i=1;i<=n;i++) check(i);
    	while(!q.empty()){
    		int x=q.top();q.pop();res[x]=c--;
    //		printf("%d
    ",x);
    		modify(1,x,mp(-INF,0));
    		check(query(1,max(x-m,1),x).se);
    		check(query(1,x,min(x+m,n)).se);
    	} for(int i=1;i<=n;i++) printf("%d
    ",res[i]);
    	return 0;
    }
    
  • 相关阅读:
    Git 命令 stash cherry-pick reset rebase
    【操作系统】不同语言的线程实现机制对比及数据库锁问题
    【数据结构】搜索二叉树(BST)和普通二叉树的序列化与反序列化
    【自制编译器】读书笔记 -- JavaCC使用的JJ文件格式
    leetcode 874 Robot Simulation
    hihocoder 编程挑战赛75
    浪漫主义的起源--以赛亚柏林
    【美团笔试 2018-4-20 】编程题-1 测例100%通过 欧拉函数求解gcd
    【POJ SOJ HDOJ】HDU 2196 Computer 树中的最长路径
    【Java 核心】多线程笔记
  • 原文地址:https://www.cnblogs.com/ET2006/p/agc001F.html
Copyright © 2020-2023  润新知