• 【AT1984】[AGC001F] Wide Swap(线段树+拓扑)


    点此看题面

    • 给定一个长度为(n)的排列(a_i),可以无限次选择交换一对满足(|a_i-a_j|=1,j-ige k)(a_i,a_j)
    • 求能得到的字典序最小的最终排列。
    • (nle5 imes10^5)

    逆置换

    考虑令(p_{a_i}=i),那么就变成可以无限次选择交换一对满足(|p_i-p_{i+1}|ge k)(p_i,p_{i+1}),要求使较小的数下标尽可能靠前。

    这样一来有一个性质就很明显了,对于两个数(x,y)满足(|x-y|<k),因为它们两个之间无法发生交换,所以(x,y)的相对顺序无法改变。

    然后再仔细一想,只要满足所有这样的(x,y)相对顺序不变,就必然能通过若干次操作得到对应的排列。

    因此,这就是我们的唯一限制条件了。

    反图拓扑排序

    一个经典问题。

    相对顺序无法改变显然就是拓扑关系,而要求较小的数下标尽可能小更是一个经典的拓扑排序问题,就是建出反图跑拓扑排序,把拓扑排序的队列改成优先队列,选出字典序最大的方案。

    无冗余建图

    注意到这里如果直接暴力建图复杂度是(O(nk))的。

    但实际上,以(p_i)向之后所有([p_i+1,p_i+k-1])中的数连边为例,我们并不需要连满所有边,假设其后第一个范围内的数是(p_x),显然(p_x)一定会向之后([p_i+1,p_i+k-1])中的所有点连边。

    那么只要从(p_x)(p_i)连边(因为要建反图),就可以做到无冗余建图了。

    具体实现中只要从后往前枚举,用值域线段树维护区间内的最小下标即可。

    代码:(O(nlogn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 500000
    #define add(x,y) (e[++ee].nxt=lnk[x],++deg[e[lnk[x]=ee].to=y])
    using namespace std;
    int n,k,a[N+5],p[N+5],ee,lnk[N+5],deg[N+5];struct edge {int to,nxt;}e[2*N+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
    ');}
    }using namespace FastIO;
    class SegmentTree
    {
    	private:
    		#define PT CI l=1,CI r=n,CI rt=1
    		#define LT l,mid,rt<<1
    		#define RT mid+1,r,rt<<1|1
    		#define PU(x) (V[x]=min(V[x<<1],V[x<<1|1]))
    		int V[N<<2];
    	public:
    		I void Build(PT) {if(l==r) return (void)(V[rt]=1e9);RI mid=l+r>>1;Build(LT),Build(RT),PU(rt);}//建树
    		I void U(CI x,CI v,PT)//单点修改
    		{
    			if(l==r) return (void)(V[rt]=v);RI mid=l+r>>1;x<=mid?U(x,v,LT):U(x,v,RT),PU(rt);
    		}
    		I int Q(CI L,CI R,PT)//区间询问最小值
    		{
    			if(L<=l&&r<=R) return V[rt];RI mid=l+r>>1;return min(L<=mid?Q(L,R,LT):1e9,R>mid?Q(L,R,RT):1e9);
    		}
    }S;
    priority_queue<int> q;I void Topo()//反图拓扑
    {
    	RI i,k,c=n;for(i=1;i<=n;++i) !deg[i]&&(q.push(i),0);W(!q.empty())
    		for(i=lnk[k=q.top()],q.pop(),a[k]=c--;i;i=e[i].nxt) !--deg[e[i].to]&&(q.push(e[i].to),0);//每次取出最大值
    	for(i=1;i<=n;++i) writeln(a[i]);//注意还原到原序列
    }
    int main()
    {
    	RI i,t;for(read(n,k),i=1;i<=n;++i) read(a[i]),p[a[i]]=i;//逆置换
    	for(S.Build(),i=n;i;S.U(p[i],i),--i)//从后往前枚举
    		(t=S.Q(max(p[i]-k+1,1),p[i]))<1e9&&add(p[t],p[i]),(t=S.Q(p[i],min(p[i]+k-1,n)))<1e9&&add(p[t],p[i]);//找到第一个范围内的数连边
    	return Topo(),clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    Bit Calculation
    Create ArcGIS maps in Power BI Desktop
    Power Apps visual for Power BI
    Create Power BI visuals by using Python
    运行 R 脚本
    Introduction to ASP.NET Core Blazor
    Tips and tricks for color formatting in Power BI
    Sharepoint Create, Update, and Delete List Items
    Power BI Office Online Server
    jQuery Organization Chart
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/AT1984.html
Copyright © 2020-2023  润新知