• [CSP-S模拟测试]:Permutation(线段树+拓扑排序+贪心)


    题目描述

    你有一个长度为$n$的排列$P$与一个正整数$K$
    你可以进行如下操作若干次使得排列的字典序尽量小
    对于两个满足$|i−j|geqslant K$且$|P_i−P_j|=1$的下标$i$与$j$,交换$P_i$与$P_j$


    输入格式

    第一行包括两个正整数$n$与$K$
    第二行包括$n$个正整数,第$i$个正整数表示$P_i$


    输出格式

    输出一个新排列表示答案
    输出共$n$行,第$i$行表示$P_i$


    样例

    样例输入:

    8 3
    4 5 7 8 3 1 2 6

    样例输出:

    1
    2
    6
    7
    5
    3
    4
    8


    数据范围与提示

    对于前$20\%$的数据满足$nleqslant 6$
    对于前$50\%$的数据满足$nleqslant 2,000$
    对于$100\%$的数据满足$nleqslant 500,000$


    题解

    这是一道暴力有$90$分的题……

    先来考虑如何换,我们每扫到一个位置,发现比它小$1$的在它右边距离大于$K$的位置就交换,不断的扫整个序列,直到无法交换为止,这时候肯定是最优的。

    交换不大于$n$次,瓶颈就在于如何快速查询交换的位置。

    首先,我们设$pos[i]$表示权值为$i$的数字在哪儿,即先当与权值与下标调换。

    那么,我们另$P_i$的字典序最小也就是另$pos[i]$的字典序最小,则操作转化为:相邻元素且权值差$geqslant K$可以交换。

    接着,问题开始抽象化,我们考虑建图……

    先来考虑暴力建边,如果$i$与后面的$j$相比,$abs(pos[i]-pos[j])<K$则其顺序已经确定,那么可以相互连边,然后跑拓扑。

    但是暴力建边显然无论是时间还是空间都会死掉(还是$90$分……)

    那么我们靠有些边是无用的,即如果$A ightarrow B$且$B ightarrow C$,那么$A ightarrow C$这条边就是无用的,但是显然我们现在的策略无法避免,考虑如何处理。

    这种情况我们一般都考虑倒着做,因为$pos[i]$连向$(pos[i]-K,pos[i])cup(pos[i],pos[i]+K)$,但是我们只需要分别连向两个区间内下标最小的那个,用线段树快速查询即可。

    时间复杂度:$Theta(nlog n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    #define L(x) x<<1
    #define R(x) x<<1|1
    using namespace std;
    struct rec{int nxt,to;}e[1000001];
    int head[500001],cnt;
    int n,K;
    int tr[2000001];
    int pos[500001],du[500001];
    int ans[500001];
    priority_queue<int,vector<int>,greater<int> > q;
    void add(int x,int y)
    {
    	e[++cnt].nxt=head[x];
    	e[cnt].to=y;
    	head[x]=cnt;
    }
    void pushup(int x){tr[x]=min(tr[L(x)],tr[R(x)]);}
    void insert(int x,int l,int r,int k,int w)
    {
    	if(l==r){tr[x]=w;return;}
    	int mid=(l+r)>>1;
    	if(k<=mid)insert(L(x),l,mid,k,w);
    	else insert(R(x),mid+1,r,k,w);
    	pushup(x);
    }
    int ask(int x,int l,int r,int L,int R)
    {
    	if(r<L||R<l)return 0x3f3f3f3f;
    	if(L<=l&&r<=R)return tr[x];
    	int mid=(l+r)>>1;
    	return min(ask(L(x),l,mid,L,R),ask(R(x),mid+1,r,L,R));
    }
    int main()
    {
    	scanf("%d%d",&n,&K);
    	memset(tr,0x3f,sizeof(tr));
    	for(int i=1;i<=n;i++){int a;scanf("%d",&a);pos[a]=i;}
    	for(int i=n;i;i--)
    	{
    		int x;
    		x=ask(1,1,n,pos[i]+1,min(pos[i]+K-1,n));
    		if(x!=0x3f3f3f3f){add(pos[i],pos[x]);du[pos[x]]++;}
    		x=ask(1,1,n,max(1,pos[i]-K+1),pos[i]-1);
    		if(x!=0x3f3f3f3f){add(pos[i],pos[x]);du[pos[x]]++;}
    		insert(1,1,n,pos[i],i);
    	}
    	int now=0;
    	for(int i=1;i<=n;i++)
    		if(!du[i])q.push(i);
    	while(!q.empty())
    	{
    		int x=q.top();q.pop();
    		ans[x]=++now;
    		for(int i=head[x];i;i=e[i].nxt)
    			if(!(--du[e[i].to]))q.push(e[i].to);
    	}
    	for(int i=1;i<=n;i++)printf("%d
    ",ans[i]);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    pandas Series和DataFrame数据类型
    numpy 统计函数与随机数
    numpy 索引
    numpy 数组复制与广播机制
    numpy 合并数组和切割数组
    numpy 添加删除去重及形状变换
    项目导入问题---讨厌的红色感叹号
    SpringMVC框架-----概述(2)
    SpringMVC框架-----概述(1)
    SpringBoot框架----概述(1)
  • 原文地址:https://www.cnblogs.com/wzc521/p/11629296.html
Copyright © 2020-2023  润新知